Skip to content

Webhooks

Trigger a workflow from an external system and read the results back.

Triggering a run

A workflow that starts with a Webhook node can be launched by an HTTP request instead of a click on the canvas:

  • POST /microservices/workflow/webhook/ — the generic trigger. Identify the workflow with workflow_id (or the webhook node's own key) and pass the inputs in the body. Returns a run id you can poll.
  • GET /microservices/workflow/{workflow_id}/node-webhook/{node_id}/ — the per-node trigger URL shown on the canvas.

The approval queue

Workflows can require a human to approve each incoming webhook request before it runs. Pending requests sit in the workflow's queue (webhook-queue/) until an owner approves or rejects them, from the app or through the API.

Authentication

Webhook trigger requests authenticate with the same session token as any other call. Callers that cannot hold a session should be given a dedicated service account.

Endpoints

MethodPathPurpose
POST/microservices/workflow/webhook/Trigger a workflow run
GET/microservices/workflow/webhook/status/Poll a run with the webhook credential
GET/microservices/workflow/{id}/node-webhook/{node_id}/Credential for a trigger node
GET/microservices/workflow/{id}/webhook-queue/The webhook run queue
POST/microservices/workflow/{id}/webhook-queue/{request_id}/Retry or cancel a queued request

POST /microservices/workflow/webhook/

Trigger a workflow run

Starts a run using a webhook credential instead of a session:

  • the workflow's own credential (webhook_id + webhook_secret from GET /microservices/workflow/{id}/) runs the whole workflow;
  • a node credential (GET …/node-webhook/{node_id}/) runs the branch that starts at that node (or the whole workflow when the node is an entry node).

Calls are queued and replayed one at a time per workflow, so a burst of calls is safe: each returns 202 with a request_id and its queue position. Poll GET /microservices/workflow/webhook/status/ with the same credential to follow the run, or watch the queue in the app.

The optional payload (max 64 KB, JSON object) reaches the triggered node as the _webhook_payload parameter. File-reference keys (file_path, file_uids and similar) are stripped — a webhook may start a run, not point it at the owner's files; file_urls is allowed for remote ingestion.

Auth: Webhook credential in the body (no session) · Rate limit: none (exempt from the anonymous limit) · In the app: Canvas → Webhook node → copy URL

Request body (application/json)

FieldTypeRequiredDescription
webhook_idstringyes
webhook_secretstringyes
payloadobjectnoInput for the run (webhook_payload accepted as an alias).
is_branchbooleannoWorkflow credential only — run a branch instead of the whole workflow.
start_node_idstringnoWith is_branch on a workflow credential — the spec node to start at.
run_to_the_endbooleannoDefault: true.
json
{
  "webhook_id": "k3Jf9Qz2p",
  "webhook_secret": "6f1e2d3c4b5a6c7d8e9f0a1b2c3d4e5f",
  "payload": {
    "file_urls": [
      "https://files.acme.com/inbox/invoice-1042.pdf"
    ],
    "file_name": "invoice-1042.pdf"
  }
}

Response 202 — Accepted — started now, or queued behind another run.

FieldTypeDescription
messagestring
request_idstring (uuid)
queuedboolean
queue_positioninteger0 when started immediately.
workflow_idstring (uuid)
json
{
  "message": "Workflow run started.",
  "request_id": "9e8d7c6b-…",
  "queued": false,
  "queue_position": 0,
  "workflow_id": "7d4c2b1a-…"
}

Response 400 — Missing credential, invalid payload, or is_branch without start_node_id.

json
{
  "error": "Invalid payload: payload is 70000 bytes, over the 65536 byte limit"
}

Response 401 — Unknown webhook id or wrong secret (indistinguishable by design).

json
{
  "error": "Invalid webhook credentials"
}

Example

bash
curl -X POST "https://api.example.com/microservices/workflow/webhook/" \
  -H "Content-Type: application/json" \
  -d '{
        "webhook_id": "k3Jf9Qz2p",
        "webhook_secret": "6f1e2d3c4b5a6c7d8e9f0a1b2c3d4e5f",
        "payload": {"file_urls": ["https://files.acme.com/inbox/invoice-1042.pdf"]}
      }'

GET /microservices/workflow/webhook/status/

Poll a run with the webhook credential

Status of the workflow and its nodes, authenticated with the same credential used to trigger it. Add include_output=true to get each node's output — for example the result of the last node.

Auth: Webhook credential in the query string (no session) · Rate limit: none (meant to be polled)

Query parameters

FieldTypeRequiredDescription
webhook_idstringyes
webhook_secretstringyes
include_outputbooleannoDefault: false.

Response 200 — Status.

FieldTypeDescription
workflow_idstring
workflow_namestring
statusstring ("IDLE", "PENDING", "RUNNING", "COMPLETED", "FAILED", "STOPPED")
current_run_versioninteger, nullable
updated_atstring (date-time)
nodesobject[]
nodes[].node_idstring
nodes[].kindstring
nodes[].statusstring
nodes[].attempt_countinteger
nodes[].started_atstring (date-time), nullable
nodes[].finished_atstring (date-time), nullable
nodes[].outputobjectOnly with include_output=true.
json
{
  "workflow_id": "7d4c2b1a-0e9f-4a3b-8c7d-6e5f4a3b2c1d",
  "workflow_name": "Invoice intake",
  "status": "COMPLETED",
  "current_run_version": null,
  "updated_at": "2026-09-22T08:05:41Z",
  "nodes": [
    {
      "node_id": "step1",
      "kind": "webhook_trigger",
      "status": "COMPLETED",
      "attempt_count": 1,
      "started_at": "2026-09-22T08:05:02Z",
      "finished_at": "2026-09-22T08:05:02Z"
    },
    {
      "node_id": "step2",
      "kind": "data_extractor",
      "status": "COMPLETED",
      "attempt_count": 1,
      "started_at": "2026-09-22T08:05:03Z",
      "finished_at": "2026-09-22T08:05:41Z"
    }
  ]
}

Response 401 — Bad credential.

json
{
  "error": "path is required"
}

Example

bash
curl "https://api.example.com/microservices/workflow/webhook/status/?webhook_id=k3Jf9Qz2p&webhook_secret=6f1e…&include_output=true"

GET /microservices/workflow/{id}/node-webhook/{node_id}/

Credential for a trigger node

Returns (creating on first read) the stable webhook credential of one node. Owner only. is_branch tells you whether a call runs just that node's branch or the whole workflow.

Auth: Session token · In the app: Canvas → Webhook node panel

Path parameters

FieldTypeRequiredDescription
idstring (uuid)yes
node_idstringyesSpec node id.

Response 200 — Credential.

FieldTypeDescription
workflow_idstring
node_idstring
webhook_idstring
webhook_secretstring
is_branchboolean
createdboolean
json
{
  "workflow_id": "7d4c2b1a-0e9f-4a3b-8c7d-6e5f4a3b2c1d",
  "node_id": "step1",
  "webhook_id": "Xp2Lm8Qa4",
  "webhook_secret": "0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d",
  "is_branch": false,
  "created": true
}

Response 404 — Node not in the spec, or not your workflow.

json
{
  "error": "path is required"
}

Example

bash
curl -X GET "https://api.example.com/microservices/workflow/<id>/node-webhook/step1/" \
  -H "Authorization: Token $FINBLADE_TOKEN"

GET /microservices/workflow/{id}/webhook-queue/

The webhook run queue

Pending and running requests in order, then the most recent finished ones (limit, max 200), with per-request file lists and outcomes.

Auth: Session token · In the app: Canvas → Queue panel

Path parameters

FieldTypeRequiredDescription
idstring (uuid)yes

Query parameters

FieldTypeRequiredDescription
limitintegernoDefault: 50.

Response 200 — Queue.

FieldTypeDescription
workflow_idstring
workflow_statusstring
countsobject
counts.pendinginteger
counts.runninginteger
counts.doneinteger
counts.failedinteger
counts.cancelledinteger
waitinginteger
requestsobject[]
requests[].idstring
requests[].file_namestring
requests[].file_urlsstring[]
requests[].statusstring ("pending", "running", "done", "failed", "cancelled")
requests[].queue_positioninteger, nullable
requests[].run_versioninteger, nullable
requests[].queued_atstring (date-time)
requests[].started_atstring (date-time), nullable
requests[].finished_atstring (date-time), nullable
requests[].duration_secondsnumber, nullable
requests[].detailstring
requests[].is_branchboolean
requests[].filesany[]
requests[].processedinteger, nullable
requests[].failedinteger, nullable
requests[].skippedinteger, nullable

Example

bash
curl -X GET "https://api.example.com/microservices/workflow/<id>/webhook-queue/" \
  -H "Authorization: Token $FINBLADE_TOKEN"

POST /microservices/workflow/{id}/webhook-queue/{request_id}/

Retry or cancel a queued request

Auth: Session token · In the app: Queue panel → Retry / Cancel

Path parameters

FieldTypeRequiredDescription
idstring (uuid)yes
request_idstring (uuid)yes

Request body (application/json)

FieldTypeRequiredDescription
actionstring ("retry", "cancel")yes

Response 200 — New state.

FieldTypeDescription
idstring
statusstring
actionstring

Response 400 — Action not applicable to the request's state.

json
{
  "error": "path is required"
}

Example

bash
curl -X POST "https://api.example.com/microservices/workflow/<id>/webhook-queue/<request_id>/" \
  -H "Authorization: Token $FINBLADE_TOKEN"

Finblade documentation