Strategies
Overview
Strategies are automated workflows that execute tasks on behalf of your users. The API models strategies in three layers:
- Strategy Catalog — the global list of available strategy templates, each with a type, schedule, and set of required connectors
- User Strategies — when a user enables a strategy from the catalog, a user strategy is created with their specific configuration and connector binding
- Strategy Runs — each execution of a user strategy, whether triggered on a schedule or manually, produces a run with status tracking and optional transaction records
Strategies come in two types:
| Type | Behavior |
|---|
active | Triggered on-demand by the user or an agent, with an optional task description |
passive | Runs automatically on a cron schedule after the user enables it |
Passive strategies create a workflow schedule on enable and run unattended. Active strategies are triggered explicitly via the runs endpoint.
Quick Start
1. Browse available strategies
curl /v0/strategies \
-H "x-juno-jwt: <token>"
The response includes every strategy in the catalog, annotated with whether the current user has enabled each one.
2. Inspect a strategy
curl "/v0/strategies/str-abc123?expand=connectors&expand=tools" \
-H "x-juno-jwt: <token>"
Use expand=connectors to see which data connectors the strategy requires, and expand=tools to see which tools it can use.
3. Enable a strategy
curl -X POST /v0/user/strategies \
-H "x-juno-jwt: <token>" \
-H "Content-Type: application/json" \
-d '{
"strategy_id": 1,
"connector_id": 5,
"configuration": {
"risk_level": "moderate",
"notify_on_complete": true
}
}'
Returns 201 Created. For passive strategies with a cron schedule, the API automatically creates a workflow schedule and triggers an initial run.
4. Trigger a run
curl -X POST /v0/user/strategies/ustr-def456/runs \
-H "x-juno-jwt: <token>" \
-H "Content-Type: application/json" \
-d '{"task": "Analyze my spending from last week"}'
The task field is optional and lets the user provide context for active strategies.
5. Check run status
curl /v0/user/strategies/ustr-def456/runs/run-789abc \
-H "x-juno-jwt: <token>"
Poll until status is completed or failed.
Strategy Catalog
Listing Strategies
Returns all strategies with per-user enablement status.
{
"strategies": [
{
"id": 1,
"external_id": "str-abc123",
"name": "Insights",
"description": "Automated financial insights from your connected accounts",
"type": "passive",
"cron": "0 8 * * *",
"base_prompt": "...",
"created_at": 1700000000000,
"is_enabled": true,
"user_strategy_id": 42,
"user_strategy_external_id": "ustr-def456",
"is_active": true
},
{
"id": 2,
"external_id": "str-xyz789",
"name": "Smart DCA",
"description": "Dollar-cost averaging with dynamic timing",
"type": "passive",
"cron": "0 9 * * 1",
"base_prompt": "...",
"created_at": 1700000000000,
"is_enabled": false,
"user_strategy_id": null,
"user_strategy_external_id": null,
"is_active": null
}
],
"_links": {
"self": { "href": "/v0/strategies" },
"user_strategies": { "href": "/v0/user/strategies" }
}
}
When is_enabled is true, the user already has this strategy active. The is_active field indicates whether the user strategy is currently running or paused.
Strategy Detail
GET /v0/strategies/{strategy_external_id}?expand=connectors&expand=tools
Returns full strategy details with optional expansion of required connectors and available tools.
{
"strategy": {
"id": 1,
"external_id": "str-abc123",
"name": "Insights",
"description": "Automated financial insights from your connected accounts",
"type": "passive",
"cron": "0 8 * * *",
"base_prompt": "...",
"created_at": 1700000000000
},
"required_connectors": [
{
"id": 5,
"external_id": "con-bank01",
"name": "Open Banking",
"description": "Connect bank accounts via open banking",
"icon_url": "https://cdn.example.com/icons/bank.svg",
"auth_type": "oauth2"
}
],
"available_tools": [
{
"id": 10,
"external_id": "tool-spend01",
"name": "Spending Analyzer",
"description": "Categorizes and analyzes transaction spending patterns",
"tool_schema": { ... }
}
],
"_links": {
"self": { "href": "/v0/strategies/str-abc123" },
"strategies": { "href": "/v0/strategies" },
"enable": { "href": "/v0/user/strategies", "method": "POST" }
}
}
The enable link is included only when the user has not yet enabled this strategy.
User Strategies
Enabling a Strategy
Request body:
{
"strategy_id": 1,
"connector_id": 5,
"configuration": {
"risk_level": "moderate"
}
}
| Field | Type | Required | Description |
|---|
strategy_id | integer | Yes | ID of the strategy from the catalog |
connector_id | integer | No | Specific connector to bind. Required if the strategy has required connectors. |
configuration | object | No | User-specific configuration for the strategy |
Response: 201 Created
{
"user_strategy": {
"id": 42,
"external_id": "ustr-def456",
"strategy": {
"id": 1,
"external_id": "str-abc123",
"name": "Insights",
"description": "...",
"type": "passive",
"cron": "0 8 * * *",
"base_prompt": "...",
"created_at": 1700000000000
},
"connector": {
"id": 5,
"external_id": "con-bank01",
"name": "Open Banking",
"description": "...",
"icon_url": "https://cdn.example.com/icons/bank.svg",
"auth_type": "oauth2"
},
"is_active": true,
"configuration": { "risk_level": "moderate" },
"created_at": 1700000060000
},
"_links": {
"self": { "href": "/v0/user/strategies/ustr-def456" },
"user": { "href": "/v0/user/me" },
"strategy": { "href": "/v0/strategies/str-abc123" },
"runs": { "href": "/v0/user/strategies/ustr-def456/runs" },
"update": { "href": "/v0/user/strategies/ustr-def456", "method": "PATCH" },
"disable": { "href": "/v0/user/strategies/ustr-def456", "method": "DELETE" }
}
}
If the strategy requires connectors that the user has not connected, the API returns 400 Bad Request with error code UST-400-001. Connect the required connectors first, or pass a valid connector_id.
Listing User Strategies
Returns all strategies the user has enabled, with embedded strategy and connector details.
Response: 200 OK
{
"user_strategies": [
{
"id": 42,
"external_id": "ustr-def456",
"strategy": { ... },
"connector": { ... },
"is_active": true,
"configuration": { "risk_level": "moderate" },
"created_at": 1700000060000
}
],
"_links": {
"self": { "href": "/v0/user/strategies" },
"strategies": { "href": "/v0/strategies" }
}
}
Getting a User Strategy
GET /v0/user/strategies/{user_strategy_external_id}
Response: 200 OK with the same user_strategy shape as above.
Updating a User Strategy
PATCH /v0/user/strategies/{user_strategy_external_id}
Request body:
{
"is_active": false,
"configuration": { "risk_level": "conservative" }
}
| Field | Type | Required | Description |
|---|
is_active | boolean | No | Pause (false) or resume (true) the strategy |
configuration | object | No | Replace the user-specific configuration |
Response: 200 OK with the updated user strategy.
Setting is_active to false pauses the workflow schedule for passive strategies. Setting it back to true resumes it.
Disabling a Strategy
DELETE /v0/user/strategies/{user_strategy_external_id}
Response: 204 No Content
Soft-deletes the user strategy and removes any associated workflow schedule. The strategy can be re-enabled from the catalog at any time.
Strategy Runs
Triggering a Run
POST /v0/user/strategies/{user_strategy_external_id}/runs
Request body (optional):
{
"task": "Analyze my spending from last week"
}
| Field | Type | Required | Description |
|---|
task | string | No | Task description for the strategy to execute |
The user_strategy_external_id path parameter accepts both ustr- prefixed user strategy IDs and str- prefixed catalog strategy IDs. When a catalog ID is provided, the API resolves it to the user’s enabled instance of that strategy.
Idempotency: Pass an Idempotency-Key header to prevent duplicate runs. If a run with the same key already exists, the API returns 208 Already Reported with the original run.
Response status codes depend on the trigger outcome:
| Status | Meaning |
|---|
| 201 Created | Run created and workflow triggered successfully |
| 202 Accepted | Run created but queued (another run is already in progress) |
| 207 Multi-Status | Run created but the workflow trigger failed (queued for retry) |
| 208 Already Reported | Duplicate idempotency key — original run returned |
Response body:
{
"run": {
"id": 100,
"external_id": "run-789abc",
"user_strategy_id": 42,
"user_strategy_external_id": "ustr-def456",
"status": "queued",
"trigger_type": "manual",
"started_at": null,
"completed_at": null,
"result": null,
"error_message": null,
"error_code": null,
"event_id": "wf_abc123",
"task": "Analyze my spending from last week",
"created_at": 1700000120000,
"transactions": null,
"user_strategy": null,
"strategy": null
},
"trigger_status": "triggered",
"_links": {
"self": { "href": "/v0/user/strategies/ustr-def456/runs/run-789abc" },
"user": { "href": "/v0/user/me" },
"user_strategy": { "href": "/v0/user/strategies/ustr-def456" },
"runs": { "href": "/v0/user/strategies/ustr-def456/runs" },
"cancel": { "href": "/v0/user/strategies/ustr-def456/runs/run-789abc", "method": "DELETE" }
}
}
Triggering a run on an inactive (paused) strategy returns 400 Bad Request with error code RUN-400-002. Resume the strategy first via PATCH.
Monitoring Run Status
GET /v0/user/strategies/{user_strategy_external_id}/runs/{run_external_id}
Use expand=transactions to include any transactions created by the run.
{
"run": {
"id": 100,
"external_id": "run-789abc",
"user_strategy_id": 42,
"user_strategy_external_id": "ustr-def456",
"status": "completed",
"trigger_type": "manual",
"started_at": 1700000121000,
"completed_at": 1700000180000,
"result": { "insights_generated": 3 },
"error_message": null,
"error_code": null,
"event_id": "wf_abc123",
"task": "Analyze my spending from last week",
"created_at": 1700000120000,
"transactions": null,
"user_strategy": null,
"strategy": null
},
"_links": {
"self": { "href": "/v0/user/strategies/ustr-def456/runs/run-789abc" },
"user": { "href": "/v0/user/me" },
"user_strategy": { "href": "/v0/user/strategies/ustr-def456" },
"runs": { "href": "/v0/user/strategies/ustr-def456/runs" },
"cancel": { "href": "/v0/user/strategies/ustr-def456/runs/run-789abc", "method": "DELETE" }
}
}
Run History
GET /v0/user/strategies/{user_strategy_external_id}/runs?status=completed&limit=10&offset=0
| Parameter | Type | Default | Description |
|---|
status | string | — | Filter by run status |
limit | integer | 20 | Maximum results (max 100) |
offset | integer | 0 | Pagination offset |
expand | string | — | Expand related resources: transactions, user_strategy, strategy, user_strategy.strategy |
Response: 200 OK
{
"runs": [ ... ],
"total": 47,
"_links": {
"self": { "href": "/v0/user/strategies/ustr-def456/runs" },
"user_strategy": { "href": "/v0/user/strategies/ustr-def456" }
}
}
Cross-Strategy Run History
To list runs across all enabled strategies:
GET /v0/user/runs?limit=20&offset=0
Accepts the same status, limit, offset, and expand parameters. The expand options user_strategy, strategy, and user_strategy.strategy are useful here to identify which strategy produced each run.
Cancelling a Run
DELETE /v0/user/strategies/{user_strategy_external_id}/runs/{run_external_id}
Response: 204 No Content
Only runs with status queued can be cancelled. Attempting to cancel a run in any other state returns 400 Bad Request with error code RUN-400-001.
Getting a Run by ID
When you have a run ID but not the parent strategy ID:
GET /v0/user/runs/{run_external_id}
Accepts the same expand parameter as the nested endpoint. The API verifies the run belongs to the authenticated user and returns 403 Forbidden if not.
Endpoints
| Method | Path | Description | Status Codes |
|---|
| GET | /v0/strategies | List all strategies with user enablement status | 200 |
| GET | /v0/strategies/{id} | Get strategy detail with optional expand=connectors,tools | 200, 404 |
| GET | /v0/user/strategies | List user’s enabled strategies | 200 |
| POST | /v0/user/strategies | Enable a strategy | 201, 400, 404, 409 |
| GET | /v0/user/strategies/{id} | Get an enabled strategy | 200, 403, 404 |
| PATCH | /v0/user/strategies/{id} | Update configuration or pause/resume | 200, 403, 404 |
| DELETE | /v0/user/strategies/{id} | Disable a strategy | 204, 403, 404 |
| GET | /v0/user/strategies/{id}/runs | List runs for a strategy | 200, 403, 404 |
| POST | /v0/user/strategies/{id}/runs | Trigger a new run | 201, 202, 207, 208, 400, 403, 404 |
| GET | /v0/user/strategies/{id}/runs/{run_id} | Get a specific run | 200, 403, 404 |
| DELETE | /v0/user/strategies/{id}/runs/{run_id} | Cancel a queued run | 204, 400, 403, 404 |
| GET | /v0/user/runs | List all runs across strategies | 200 |
| GET | /v0/user/runs/{run_id} | Get a run by ID (cross-strategy) | 200, 403, 404 |
Integration Patterns
Building a Strategy Selection UI
- Fetch the catalog with
GET /v0/strategies
- For each strategy, check
is_enabled to show the user’s current state
- When the user selects a strategy, fetch its detail with
expand=connectors to show prerequisites
- If required connectors are not connected, guide the user through connector setup first
- Enable the strategy with
POST /v0/user/strategies
Polling Run Status
After triggering a run, poll the run endpoint until status reaches a terminal state:
state = POST /v0/user/strategies/{id}/runs { task: "..." }
run_id = state.run.external_id
while state.run.status in ["queued", "running"]:
wait(2 seconds)
state = GET /v0/user/strategies/{id}/runs/{run_id}
if state.run.status == "completed":
handle_success(state.run.result)
else:
handle_failure(state.run.error_message, state.run.error_code)
Run Lifecycle
Strategy Lifecycle
Idempotent Run Triggers
For systems that may retry requests (webhooks, queues), use the Idempotency-Key header:
curl -X POST /v0/user/strategies/ustr-def456/runs \
-H "x-juno-jwt: <token>" \
-H "Idempotency-Key: evt_unique_12345" \
-H "Content-Type: application/json" \
-d '{"task": "Process daily report"}'
If the same key is sent again, the API returns 208 Already Reported with the original run instead of creating a duplicate.
Error Handling
All errors follow RFC 7807 Problem Details format:
{
"type": "https://api.sumvin.com/errors/str-404-001",
"title": "Strategy Not Found",
"status": 404,
"detail": "Strategy str-abc123 not found",
"instance": "/v0/strategies/str-abc123",
"error_code": "STR-404-001"
}
Strategy Errors (STR)
| Code | Status | Description | Recovery |
|---|
STR-404-001 | 404 | Strategy not found in the catalog | Verify the strategy external ID via GET /v0/strategies |
STR-409-001 | 409 | Strategy already enabled for this user | Use the existing user strategy, or disable and re-enable |
STR-400-001 | 400 | Invalid external ID format | Use str- prefix for catalog strategies, ustr- for user strategies |
User Strategy Errors (UST)
| Code | Status | Description | Recovery |
|---|
UST-404-001 | 404 | User strategy not found | Verify the user strategy external ID via GET /v0/user/strategies |
UST-403-001 | 403 | User strategy belongs to another user | Verify you are using the correct ID |
UST-400-001 | 400 | Missing required connector | Connect the required connectors before enabling the strategy |
Strategy Run Errors (RUN)
| Code | Status | Description | Recovery |
|---|
RUN-404-001 | 404 | Strategy run not found | Verify the run external ID via the runs list endpoint |
RUN-403-001 | 403 | Strategy run belongs to another user | Verify you are using the correct run ID |
RUN-409-001 | 409 | Another run is already in progress | Wait for the current run to complete, or cancel it |
RUN-400-001 | 400 | Cannot cancel run (not in queued status) | Only queued runs can be cancelled |
RUN-400-002 | 400 | Strategy is not active | Resume the strategy via PATCH before triggering a run |
RUN-208-001 | 208 | Duplicate idempotency key | The original run is returned in the response body |
Reference Tables
Strategy Types
| Value | Description |
|---|
active | Triggered on-demand by the user or an agent |
passive | Runs automatically on a cron schedule |
Run Statuses
| Value | Description |
|---|
queued | Run created, waiting for workflow pickup |
running | Workflow is currently executing |
completed | Workflow finished successfully |
failed | Workflow encountered an error |
cancelled | Run was cancelled before execution |
Trigger Types
| Value | Description |
|---|
scheduled | Triggered by the cron schedule |
manual | Triggered by an explicit API call |
Trigger Statuses
Returned in the trigger_status field of the trigger response:
| Value | Description |
|---|
triggered | Workflow was dispatched successfully |
queued | Run was created but another run is in progress — will execute when the current run finishes |
trigger_failed | Workflow dispatch failed — run is queued for automatic retry |
Connector Auth Types
| Value | Description |
|---|
oauth | OAuth 1.0 authentication |
oauth2 | OAuth 2.0 authentication |
api_key | API key authentication |
Expand Parameters
| Endpoint | Expand Options |
|---|
GET /v0/strategies/{id} | connectors, tools |
GET .../runs | transactions, user_strategy, strategy, user_strategy.strategy |
GET .../runs/{id} | transactions, user_strategy, strategy, user_strategy.strategy |
GET /v0/user/runs | transactions, user_strategy, strategy, user_strategy.strategy |
GET /v0/user/runs/{id} | transactions, user_strategy, strategy, user_strategy.strategy |
Next Steps