# Sequence API

> Programmable money platform. The API reads account state and executes money flows (rules and transfers) on behalf of authenticated organizations. Account setup, identity verification, bank linking, and rule creation happen in the dashboard at https://app.getsequence.io -- the API is for programmatic execution and observation.

Base URL: `https://api.getsequence.io/platform/v1`

## API reference
- [OpenAPI 3.1 spec](https://api.getsequence.io/platform/v1/openapi): Machine-readable spec (YAML) for every endpoint, schema, and error code. Download with `curl -o sequence-openapi.yaml https://api.getsequence.io/platform/v1/openapi` and feed it to your agent or codegen tool.
- [Interactive HTML reference](https://api.getsequence.io/platform/): Browser-rendered docs.

## Endpoints

### Accounts
- `GET /accounts` -- list account summaries (income sources, pods, external). Returns every account in the org regardless of the key's `READ_ACCOUNTS` resource scope; summaries carry only non-sensitive metadata (no balances or account/routing numbers). Filter by type. Requires `READ_ACCOUNTS`.
- `GET /accounts/{id}` -- fetch one account with its details and current balance. Routing and bank account numbers are masked to the last 4 digits (e.g. `••••4892`); the full values are never returned. Enforces the key's `READ_ACCOUNTS` resource scope -- returns `403` for accounts outside it. Requires `READ_ACCOUNTS`.
- `POST /accounts` -- create a Sequence-managed account: a `POD` (savings/goal bucket) or an `INCOME_SOURCE` (entry point where outside money lands). Owned by a beneficiary (`beneficiaryId`; defaults to the org's default beneficiary when omitted). `savingsTargetInCents` is pods-only. Returns the full account (zero balance). Requires `CREATE_ACCOUNTS` permission.
- `GET /accounts/{accountId}/transfers` -- list transfers for an account, newest first. Optional filter by `executionMode`, defaults to LIVE. Requires `READ_TRANSFERS`.

### Rules
- `GET /rules` -- list rules (lightweight). Filter by source account. Requires `READ_RULES`.
- `GET /rules/{id}` -- fetch one rule with full steps, conditions, actions. Requires `READ_RULES`. Returns `INVALID_RULE` if the rule's shape isn't representable in the public API (treat as read-only metadata).
- `POST /rules/{id}/trigger` -- trigger a rule or a dry-run rule simulation. Returns `202 Accepted` with `executionId`. Async -- poll the execution and transfers endpoints for outcome. Simulations do not move real money. Requires `TRIGGER_RULES` permission.
- `GET /rules/{ruleId}/executions` -- list executions for a rule. Optional filter by `executionMode`, defaults to LIVE. `LIVE` — real money movement, `SIMULATION` — dry run rule executions, `ALL` — both. Requires `READ_RULES`.
- `GET /rules/{ruleId}/executions/{id}` -- poll one execution for status and resulting transfer IDs. Requires `READ_RULES`.

### Activity
- `POST /transfers` -- create a transfer between two accounts. Async -- poll the transfer endpoint for status. Set `simulation: true` for a dry run that returns a simulated transfer immediately without moving actual money. Requires a `MANUAL_TRANSFER` entry matching `sourceAccountId` and `destinationAccountId` (and `maxAmount` if set).
- `GET /transfers` -- list transfers for one or more accounts. Optional filter by `executionMode`, defaults to LIVE. Requires `READ_TRANSFERS`.
- `GET /transfers/{id}` -- fetch one transfer. Requires `READ_TRANSFERS` on either the source or destination account.
- `GET /external-transactions` -- list transactions on connected external accounts (Plaid/Finicity) that Sequence does not manage; visibility only. Filter by `direction`, `status`, and `from`/`to` (up to 90 days back). Data refreshes roughly every 24h. Requires `READ_TRANSFERS`.
- `GET /card-transactions` -- list settled card transactions (purchases and refunds) for cards funded by a pod (`DEBIT_CARD` and `OMNI_CARD`). Filter by `cardId` and `from`/`to`. Requires `READ_TRANSFERS`.

## Authentication
- Header: `Authorization: Bearer <api_key>`. Keys come from Settings -> API Keys in the dashboard.
- Each key carries scoped permissions; requests outside scope return `403 ACCESS_DENIED`.
- Permission scopes: `READ_ACCOUNTS`, `CREATE_ACCOUNTS`, `READ_TRANSFERS`, `READ_RULES`, `TRIGGER_RULES`, `MANUAL_TRANSFER`. Each scope is bound to a specific list of resource IDs (or "all"). `CREATE_ACCOUNTS` is org-scoped (on/off, no resource list).
- Never embed keys in client-side code or commit them to source control.
- Use a **separate API key per agent / automation** -- enables per-agent revocation, clean audit attribution, and per-key rate-limit isolation. Suggested naming: `<tool>-<purpose>` (e.g. `claude-rule-payroll`).

## MCP (Model Context Protocol)
Sequence runs an MCP server so AI assistants can call the API directly instead of writing HTTP code. Every endpoint above is exposed as an MCP tool automatically, so tools stay in sync with the API.
- Server URL: `https://app.getsequence.io/api/mcp`. Transport: Streamable HTTP.
- Auth: the same API key as REST, sent as an `Authorization: Bearer <api_key>` header. Scopes apply identically -- the assistant can only do what the key allows.
- Claude Code: `claude mcp add --transport http sequence https://app.getsequence.io/api/mcp --header "Authorization: Bearer <api_key>"`.
- Cursor / other Streamable-HTTP clients: add `{ "mcpServers": { "sequence": { "url": "https://app.getsequence.io/api/mcp", "headers": { "Authorization": "Bearer <api_key>" } } } }`.
- Claude Desktop has no native Streamable-HTTP support; bridge via `mcp-remote` with the absolute `npx` path (`{ "command": "<npx-path>", "args": ["mcp-remote", "https://app.getsequence.io/api/mcp", "--header", "Authorization: Bearer <api_key>"] }`).
- Full per-client setup is in the rendered docs ("Connect via MCP" section) and the dashboard's MCP page.

## Concepts agents should know
- A **rule** = trigger + optional conditions + actions. Rules must be enabled by a human in the dashboard before they can run; the API cannot enable rules.
- A rule's trigger type can be `MANUAL` (on-demand), `SCHEDULED`, or `ON_FUNDS_TRANSFERRED`. `POST /rules/{id}/trigger` works for any trigger type -- it runs the rule on demand regardless of its configured schedule or condition.
- Triggering a rule is **async**: `POST /rules/{id}/trigger` returns `202` with an `executionId`. Poll `/rules/{ruleId}/executions/{id}` for status and resulting transfer IDs. `executeAmount` (cents) drives amount-derived actions and means the same thing live or simulated, so a dry run with an `executeAmount` previews what a live trigger with that `executeAmount` would move. Dry run: `simulation: true` (optional `executeAmount`); poll the same endpoints for simulation results. Dry runs can also override balances the account does not hold: the base a dry run works from is `(simulatedSourceBalance ?? executeAmount ?? realBalance) + (simulatedIncomingFunds ?? 0)`. `simulatedSourceBalance` and `executeAmount` replace the source balance; `simulatedIncomingFunds` adds to it, because it models a deposit that has not settled yet -- use it to preview an `ON_FUNDS_TRANSFERRED` rule. A live run never adds: it reads a real balance that already includes the deposit that triggered it, which is why only the simulated path sums two numbers. Both simulated fields are ignored on live triggers. A dry run is allowed on a deactivated (disabled) rule, so you can preview a freshly-created rule before activating it; live triggers still return `RULE_DEACTIVATED` on a disabled rule.
- Creating a transfer is **async**: `POST /transfers` returns the new transfer in a `PROCESSING` state. Poll `GET /transfers/{id}` for terminal status. Dry run: set `simulation: true` to simulate a transfer without moving actual money; transfer status is set to `COMPLETE` immediately.
- A newly created transfer ID may briefly return `404` from `GET /transfers/{id}` immediately after `POST /transfers` due to eventual consistency. Retry with backoff for a few seconds before treating it as a real miss.
- All write endpoints (`POST /rules/{id}/trigger`, `POST /transfers`) accept and require an `Idempotency-Key` header. Reusing the same key with different parameters returns `IDEMPOTENCY_KEY_MISMATCH`.
- Credit card and debit card transactions are excluded from transfer listings.
- **Polling cadence.** Rate limit is 100 req/min per key. Recommended intervals: balances every 6h (provider refreshes ~daily); transfers no more often than every 1h, and every 6h covers most use cases (use `created_at` filters to fetch only new rows). On `429 RATE_LIMIT_EXCEEDED`, honor the `Retry-After` header and back off with jitter.
- Transfer `description` is the ACH label that appears on the recipient's statement -- max 10 characters, letters/digits/spaces only (NACHA constraint). Not a freeform memo.

## Errors
Errors return a JSON body `{ "error": { "code": "<CODE>", "message": "..." } }`. Common codes: `ACCESS_DENIED`, `IDEMPOTENCY_KEY_MISMATCH`, `RULE_DEACTIVATED`, `INVALID_RULE`, `MAXIMUM_AMOUNT_EXCEEDED`.
