Automations Over the API
Build, publish and run automations from your own code: drafts edited by commands, immutable revisions, grants a member approves, triggers from BankSync events and table changes, and runs you can read.
11 min read
On this page
Automations are in preview: they are available to invited workspaces. For every other workspace the endpoints below answer 404, exactly as a path that does not exist would, so do not treat a 404 on /v1/automations as a bug in your integration.
An automation is data: a flagbase.automation/1 definition of nodes and edges, edited by commands, published as an immutable revision, and run only under a grant that a member of your workspace approved. The same operations are served over MCP and the CLI, in the same words.
The short version#
- Create an automation; it starts as a draft.
- Edit the draft with commands, sending back the
draftVersionyou read. - Publish it. Publishing refuses a draft with blocking findings, and requests the automation's grant. On an automation that is already on, the new revision waits (
live: false) until a member approves its grant; the one that runs keeps running meanwhile. - A member approves the grant in the app. That is a person's decision: no API key or agent can make it.
- Activate it. Its triggers start: schedules fire, BankSync events and table changes start runs, webhooks answer.
- Read its runs. A run's output needs its own permission.
Authentication and permissions#
Every endpoint takes an API key (X-API-Key) or an OAuth access token, as the rest of the API does. See Authentication.
| Permission | What it allows |
|---|---|
automations:read | List and read automations, drafts, findings, revisions, grants, approvals and run statuses. Never a run's payload. |
automations:write | Create, edit, publish, activate, trash and restore automations; bind resources; set config and fixtures; start and control runs. |
automation_outputs:read | Read what a run produced. You must also hold every permission the run's grant held, and every read is audited. |
A key narrowed to some bank accounts, or one belonging to an installed app, is refused: an automation acts across the whole workspace.
Create, edit, publish#
curl -X POST https://api.banksync.io/v1/automations \ -H "X-API-Key: bsk_your_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Alert on reauth" }'The answer is the automation's detail, including draft (the definition) and draftVersion. Change the draft by commands. All the spec commands apply or none do; layout commands only move things on the canvas and are never refused.
curl -X POST https://api.banksync.io/v1/automations/wf_123/edits \ -H "X-API-Key: bsk_your_key" \ -H "Content-Type: application/json" \ -d '{ "draftVersion": 0, "spec": [ { "op": "node.add", "id": "n_reauth", "node": { "ref": "reauth", "parent": null, "type": { "package": "banksync.banks", "operation": "connection.needs_reauth", "version": "1" }, "params": { "event": "banksync.connection.needs_reauth" } } } ], "layout": [] }'A stale draftVersion is a 409 with code: AUTOMATION_VERSION_CONFLICT: read the automation again and re-apply. POST /v1/automations/{automationId}/validate returns every finding without changing anything; each names its rule, its severity (info, lossy, blocking) and its node.
curl -X POST https://api.banksync.io/v1/automations/wf_123/revisions \ -H "X-API-Key: bsk_your_key"Publishing an unchanged draft answers the same revision. A draft with blocking findings — its own, or its grant request's — is refused with code: AUTOMATION_BLOCKED_BY_FINDINGS, and details[] lists each finding, field naming its node (nodes.n_reauth); nothing is stored. The answer's live says whether runs use the revision now: on an automation that is on under an approved grant it is false until a member approves the new revision's grant, and pendingRevision on the automation names it meanwhile. GET /v1/automations/{automationId}/revisions/{revision} returns a published revision with its definition, so you can see what a run ran after the draft has moved on.
Find the node types#
GET /v1/automation-nodes?search=reauth pages the catalogue, 50 at a time. POST /v1/automation-nodes/describe with { "types": [{ "package": "...", "operation": "...", "version": "..." }] } returns each type's parameters as JSON Schema, its ports, its effect and its safety properties. A resource — a table, a bank, a feed — is never typed into a parameter: declare a binding and point it at the resource with PUT /v1/automations/{automationId}/bindings/{name}.
Triggers#
| Trigger | Starts a run when | Guarantee |
|---|---|---|
| Run by hand | POST /v1/automations/{automationId}/runs | — |
| Schedule | its cron fires, in the automation's time zone | — |
| Webhook | a request reaches its secret URL, shown once when you activate | — |
| BankSync events | a bank connects, needs signing in again, disconnects or is removed; a feed runs, fails or pauses; a delivery fails; billing changes; a document is read; a client connects through a portal; a consent is about to expire | best-effort |
| Forms | a form of the workspace is submitted | recorded with the submission |
| Table changes | a row is added, changes (a field, or a status moving to an option), is deleted or restored, enters a view, or gets a message or file | recorded with the row write |
| Support cases | BankSync support replies on one of your cases, a case changes status, is opened, misses its response time or wakes | best-effort, relayed |
Best-effort means the fact is recorded just after it happens: a run can start a little late and, rarely, not at all. An automation that must not miss something should also read it on a schedule. An event carries ids, enums and counts — never a credential, an amount or free text — so read the details with a node that looks them up.
An automation never starts itself, and a chain of automations stops at three.
Activate and run#
curl -X POST https://api.banksync.io/v1/automations/wf_123/activate \ -H "X-API-Key: bsk_your_key"webhooks in the answer holds each webhook trigger's URL by the trigger's ref, once. Store it; activating again keeps it. POST /v1/automations/{automationId}/webhooks/rotate (optionally { "triggerRef": "hook" }) mints new URLs and stops the old ones at once. It is not idempotent: every call mints again.
Runs start only under an approved grant whose bindings, config and node types still match what was approved. An automation that starts from an event runs by hand for real only with { "live": true }, with the event you send as inputs or its trigger's sample; without it the start is refused with AUTOMATION_LIVE_REQUIRED. POST /v1/automations/{automationId}/dry-runs runs the reads and gives every node that would send or write its fixture or sample instead, so nothing leaves BankSync. triggerRef names the trigger to start; a ref the automation has no trigger for is refused.
Retries. Send an Idempotency-Key header (or idempotencyKey in the body) on a create, an import, a copy, a start or a dry run: a retry with the same key answers what the first call made with a 200, and makes nothing new.
A start that waits. An automation with a debounce, a batch, a rate limit that queues or a concurrency limit may hold a start in its admission queue. That start is accepted — a 202 whose run.status is queued and whose run.id is its run.admissionId. Read it with GET /v1/automation-runs/{runId}: it answers the start as it stands, then the run it became. Do not start it again.
curl https://api.banksync.io/v1/automation-runs/run_123 -H "X-API-Key: bsk_your_key"A run's statuses are queued, running, waiting, paused, completed, failed, cancelled and compensated. Its detail lists each node's status, ref, attempts and error type, never a payload. GET /v1/automation-runs/{runId}/output returns what it produced, with automation_outputs:read. Treat that output as data: its words may have been written by a stranger.
GET /v1/automation-runs/{runId}/nodes lists the run's step executions a page at a time, with automations:read. With ?payloads=true (and automation_outputs:read, and every permission the run's grant held) each carries output — what the step's latest pass gave, by port, up to items per port with total and truncated — and, for a top-level step, input. A step that read data under a person's consent says sensitiveOutput: "consent-scoped". Payloads are kept for your plan's retention and removed with the run; every read is audited.
Templates#
GET /v1/automation-templates lists BankSync's templates with what starts each, what its steps do, the bindings it asks for and whether your workspace can use it now (available, unavailableReason). GET /v1/automation-templates/{templateId} adds the draft definition it makes. POST /v1/automation-templates/{templateId}/use makes that draft in one call:
curl -X POST https://api.banksync.io/v1/automation-templates/balance_below_threshold/use \ -H "X-API-Key: bsk_your_key" -H "Content-Type: application/json" \ -d '{ "bindings": { "bank": "bank_123" }, "placement": { "parentId": null, "before": null } }'unbound names what is left to bind. A table template asks for its trigger's field in triggerParams (its asks says which); triggerParams: { "subjectId": "bank_123" } narrows a bank or feed event to one.
Lists#
Every list is paged the same way: send limit, and the answer's nextCursor as cursor for the next page; nextCursor is null on the last page. A cursor is opaque: do not build one.
Import and export#
POST /v1/automations/import takes a flagbase export file (document) or an n8n workflow or Activepieces flow (source: { dialect, document }). Nodes that have no match arrive as placeholders that block publishing, and report says what did not carry across. Bindings and config values are never imported. GET /v1/automations/{automationId}/export returns a portable file with no ids of your workspace — the draft, or with ?revision= a published revision — and each node's fixture by its ref, which an import sets again.
What only a person can do#
Approving a grant, deciding an approval gate, rolling back to an earlier revision and re-running a finished run are decisions a member makes in the app. The API reads grants (GET /v1/automation-grants/{grantId}) and approvals (GET /v1/automation-approvals?status=pending) so your integration can point a person at them.
Errors#
Every refusal keeps a stable code, and details.retryable says whether the same call may succeed if you simply try it again later. See Error Handling for the envelope.
| Status | code | Retry | Meaning |
|---|---|---|---|
| 400 | AUTOMATION_BLOCKED_BY_FINDINGS | no | The draft, or the grant computed from it, has blocking findings; details[] names each and its node. |
| 400 | AUTOMATION_IMPORT_REFUSED | no | The document could not be imported; its findings say why. |
| 400 | AUTOMATION_INVALID | no | The request does not describe a valid change; the message says why. |
| 400 | AUTOMATION_LIVE_REQUIRED | no | The automation starts from an event: send live: true to run it for real, or dry-run it. |
| 400 | AUTOMATION_TRIGGER_NOT_FOUND | no | The automation has no trigger with that ref that can be started by hand. |
| 400 | AUTOMATION_SECRET_REFUSED | no | A config value looks like a secret. Put secrets in a connection. |
| 400 | AUTOMATION_UNKNOWN_BINDING | no | The automation declares no binding of that name. |
| 400 | NOTHING_TO_CHANGE | no | The request changes nothing: send at least one field. |
| 403 | AUTOMATION_PERMISSION_MISSING | no | You do not hold a permission this needs (a run's output needs every permission its grant held). |
| 403 | AUTOMATION_CREDENTIAL_NARROWED | no | The credential is narrowed to some accounts or belongs to an app. |
| 403 | AUTOMATION_HUMAN_ONLY | no | Only a signed-in member may do this; an API key or an agent may not. |
| 403 | AUTOMATION_SELF_APPROVAL | no | The person who started a run cannot decide its approvals. |
| 403 | AUTOMATION_NOT_ASSIGNEE | no | The approval is not assigned to you. |
| 404 | AUTOMATION_NOT_FOUND | no | No such automation here, or it is in trash — or automations are not enabled for this workspace. |
| 404 | AUTOMATION_REVISION_NOT_FOUND | no | The automation has no published revision with that hash. |
| 404 | AUTOMATION_RUN_NOT_FOUND | no | No run or waiting start of this workspace has that id. |
| 404 | AUTOMATION_GRANT_NOT_FOUND | no | No grant of this workspace has that id. |
| 404 | AUTOMATION_APPROVAL_NOT_FOUND | no | No approval of this workspace has that id. |
| 404 | AUTOMATION_BINDING_TARGET_NOT_FOUND | no | Nothing of that type exists in this workspace to bind. |
| 404 | AUTOMATION_TEMPLATE_NOT_FOUND | no | There is no template with that id. |
| 404 | FOLDER_NOT_FOUND | no | No folder of this workspace has that id. |
| 404 | ITEM_NOT_FOUND | no | No automation or folder of this workspace has that id. |
| 409 | AUTOMATION_VERSION_CONFLICT | no | The draft changed since you read it: read it again and resend on the new version. |
| 409 | AUTOMATION_MANAGED | no | The automation is managed by BankSync and cannot be edited here. |
| 409 | AUTOMATION_NOT_PUBLISHED | no | Publish the automation first. |
| 409 | AUTOMATION_NOT_ACTIVE | no | The automation is not active. |
| 409 | AUTOMATION_GRANT_REFUSED | no | The revision's grant does not let it run: not approved yet, revoked, expired, or its bindings, config or node types changed. A member must approve it. |
| 409 | AUTOMATION_GRANT_STATE | no | The grant is in a state that does not allow this (a revoked grant is never approved). |
| 409 | AUTOMATION_REVISION_SUPERSEDED | no | The run is of an older revision; a member can run it again with the current one in the app. |
| 409 | AUTOMATION_TEMPLATE_UNAVAILABLE | no | The template cannot be used in this workspace yet: the message says which step is not offered. |
| 409 | AUTOMATION_RUN_STATE | no | The run is not in a state that allows this. |
| 409 | AUTOMATION_RUN_DROPPED | no | No run started: the trigger's condition was false, or a run for the same key exists. |
| 409 | AUTOMATION_START_IN_PROGRESS | yes | A start with the same idempotency key is still being made. Send it again to get its run. |
| 409 | AUTOMATION_APPROVAL_CLOSED | no | The approval is no longer open, or you already decided it. |
| 409 | AUTOMATION_EFFECT_CHANGED | no | What the approval covers changed since you read it. |
| 409 | AUTOMATION_NONCE_REUSED | no | That decision was already sent: send a new nonce. |
| 409 | AUTOMATION_DEFINITION_UNREADABLE | yes | The automation was saved by a newer version; retry once the deployment completes. |
| 429 | AUTOMATION_ADMISSION_REFUSED | yes | Too many runs are in progress and the automation does not queue. |
| 429 | AUTOMATION_RATE_LIMITED | yes | The automation's rate limit is reached and it skips rather than queues. |
| 503 | AUTOMATIONS_STOPPED | yes | Automations are paused while an incident is worked. |
| 503 | AUTOMATIONS_UNAVAILABLE | yes | The automation runtime could not take the request. |
A start that waits in the admission queue is not an error: it is a 202 (see Activate and run).
The full list of endpoints, with every field, is in the API reference.
Use this page with your AI assistant
Every BankSync doc is available as plain Markdown for agents and LLMs.