API reference

Workflows

Build multi-step contact flows as drafts, validate and publish them, and run them for one contact.

A workflow describes, step by step, what happens to one contact: call with an agent, read the extracted data, wait, retry, call an HTTP endpoint, end with an outcome. You edit a draft, validate it and publish it. Publishing freezes the draft as a numbered version that never changes. You then run a version for a single contact from this page, or for a list through a workflow campaign. See Campaigns and workflows.

All routes need Authorization: Bearer <key>. Reading needs the viewer role; changing needs member; the webhook test needs admin.

OperationMethod and path
Create a workflowPOST /workflows
List workflowsGET /workflows
Get a workflowGET /workflows/{workflow_id}
Update a workflowPATCH /workflows/{workflow_id}
Delete a workflowDELETE /workflows/{workflow_id}
Get the draftGET /workflows/{workflow_id}/draft
Save the draftPUT /workflows/{workflow_id}/draft
Validate the draftPOST /workflows/{workflow_id}/validate
Publish the draftPOST /workflows/{workflow_id}/publish
Get a published versionGET /workflows/{workflow_id}/versions/{version}
Run a workflow for one contactPOST /workflows/{workflow_id}/run
Send a test webhookPOST /workflows/{workflow_id}/webhook:test
List node typesGET /workflow-node-types

Error format

The workflow, workflow campaign and workflow execution routes use their own error body, different from the rest of the API:

json
{"detail": {"code": "revision_conflict", "message": "expected_revision 0 is stale; the draft is at revision 1", "current_revision": 1}}

code is a stable identifier, message is for people, and some errors add fields such as current_revision, issues or fields. Request validation problems answer 422 with code validation_failed. An unknown id answers 404 with code not_found. Authentication, role and rate-limit errors (401, 403, 429) still use the standard body {"error": <status>, "message": "..."}.

The definition

A definition is a JSON object of at most 512 KB.

FieldTypeDescription
entry_node_idstringThe start node.
on_no_matchstringNode to go to when a node's cases do not match and the node has no fallback of its own.
nodesarrayThe steps.
nodes[].idstringLower-case letters, digits and underscores, up to 64 characters.
nodes[].typestringstart, agent, extraction, api, time, retry, aisensy_whatsapp or end.
nodes[].namestringOptional label.
nodes[].configobjectSettings of the node type. List node types returns every parameter with its default and bounds.
nodes[].casesarrayOrdered branches: {"when": <condition>, "then": {"to": "<node id>"}}. The first match wins. time nodes use a single to instead.

A condition is one of {"always": true}, {"cmp": <operator>, "left": <operand>, "right": <operand>}, {"all": [...]}, {"any": [...]}, {"not": <condition>}, {"exists": {"var": "..."}} or {"not_exists": {"var": "..."}}. Comparison operators are ==, !=, >, >=, <, <=, in, not_in and contains. An operand is {"var": "call.status"}, {"const": <value>} or arithmetic {"op": "+", "args": [...]}. Variables live in the namespaces entry, call, extraction, response, state, node and whatsapp.

The start node declares the contact fields the workflow accepts under config.fields, each with a name, a type (string, number, boolean, timestamp, phone, email, url, json) and required. Four system fields always exist: reference_id, mobile_number, name and email.

Limitation: The aisensy_whatsapp node sends through a third-party WhatsApp provider and only works when the operator has configured that provider. See Messaging.

Operations

Create a workflow

POST /workflows

Creates a workflow. Its draft starts out empty, at revision 0.

Auth: API key or session token. Role: member and up.

ParameterInTypeRequiredDescription
namebodystringyes1 to 120 characters.

Request

bash
curl -X POST https://api.sevennadi.si/workflows \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"name": "Loan follow-up"}'

Response 201 Created

json
{
  "id": "2ab95da4-50dd-4492-a355-793ec7d810c5",
  "name": "Loan follow-up",
  "status": "active",
  "created_at": "2026-10-13T07:22:20Z",
  "updated_at": "2026-10-13T07:22:20Z"
}

latest_published_version is added once the workflow has been published.

Errors

StatusCodeMessage
422validation_failedname must be 1 to 120 characters
422validation_failedMalformed JSON body or The body must be a JSON object

List workflows

GET /workflows

Returns the account's workflows, newest first.

Auth: API key or session token. Role: viewer and up.

ParameterInTypeRequiredDescription
namequerystringnoCase-insensitive substring of the name.
limitqueryintegerno1 to 100. Default 20.
offsetqueryintegernoRows to skip. Default 0.

Request

bash
curl "https://api.sevennadi.si/workflows?name=loan&limit=20&offset=0" \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{
  "items": [
    {
      "id": "2ab95da4-50dd-4492-a355-793ec7d810c5",
      "name": "Loan follow-up",
      "status": "active",
      "latest_published_version": 1,
      "created_at": "2026-10-13T07:22:20Z",
      "updated_at": "2026-10-13T07:30:00Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

The workflow routes page with limit and offset, not page_number and page_size. See Pagination.

Errors

StatusCodeMessage
422validation_failedlimit: Input should be less than or equal to 100

Get a workflow

GET /workflows/{workflow_id}

Returns a workflow with its webhook settings, the state of its draft and a summary of each published version.

Auth: API key or session token. Role: viewer and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.

Request

bash
curl https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5 \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{
  "workflow": {
    "id": "2ab95da4-50dd-4492-a355-793ec7d810c5",
    "name": "Loan follow-up",
    "status": "active",
    "latest_published_version": 1,
    "created_at": "2026-10-13T07:22:20Z",
    "updated_at": "2026-10-13T07:30:00Z"
  },
  "settings": {"webhook": {"url": "https://hooks.example.test/wf", "headers": {"X-Token": "**********"}}},
  "draft": {"version": 2, "state": "draft", "revision": 0},
  "published": [
    {
      "version": 1,
      "state": "published",
      "revision": 0,
      "published_at": "2026-10-13T07:30:00Z",
      "campaigns": 1,
      "success_count": 12,
      "failure_count": 3,
      "neutral_count": 0,
      "terminal_count": 15,
      "contacts": 18
    }
  ]
}

settings is {} when no webhook is set. Header values are always masked.

Errors

StatusCodeMessage
404not_foundWorkflow not found

Update a workflow

PATCH /workflows/{workflow_id}

Renames a workflow or changes its webhook settings. settings is applied as a JSON merge patch: keys you send replace stored ones, null removes a key, and keys you leave out stay.

When an execution reaches a final status, the platform posts an execution.terminal payload to settings.webhook.url with the configured headers.

Auth: API key or session token. Role: member and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.
namebodystringno1 to 120 characters.
settings.webhook.urlbodystringnoAn https URL on port 443 or 8443 that does not point at a private or internal address.
settings.webhook.headersbodyobjectnoHeader names to string values. Send null for one header to remove it.

Request

bash
curl -X PATCH https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5 \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"settings": {"webhook": {"url": "https://hooks.example.test/wf", "headers": {"X-Token": "<your secret>"}}}}'

Response 200 OK

json
{
  "id": "2ab95da4-50dd-4492-a355-793ec7d810c5",
  "name": "Loan follow-up",
  "status": "active",
  "created_at": "2026-10-13T07:22:20Z",
  "updated_at": "2026-10-13T07:25:00Z",
  "settings": {"webhook": {"url": "https://hooks.example.test/wf", "headers": {"X-Token": "**********"}}}
}

Send {"settings": {"webhook": null}} to remove the webhook. If the URL moves to another host, stored headers are dropped unless you send them again.

Errors

StatusCodeMessage
404not_foundWorkflow not found
422validation_failedSend name and/or settings, Unknown field <name>, Unknown settings key <name>, Unknown settings.webhook key <name>
422validation_failedsettings.webhook.url is required
422invalid_webhook_urlsettings.webhook.url must use https, settings.webhook.url must use port 443 or 8443, settings.webhook.url must not point at a private or internal address
422invalid_webhook_headersHeader <name> is reserved, Invalid header name <name>, Header <name> must be a string, Header <name>: send the real value, or omit it to keep it

Delete a workflow

DELETE /workflows/{workflow_id}

Deletes a workflow. It disappears from lists and can no longer be run.

Auth: API key or session token. Role: member and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.

Request

bash
curl -X DELETE https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5 \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 204 No Content

No body.

Errors

StatusCodeMessage
404not_foundWorkflow not found

Get the draft

GET /workflows/{workflow_id}/draft

Returns the draft definition and its revision number. You need the revision to save.

Auth: API key or session token. Role: viewer and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.

Request

bash
curl https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/draft \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{"definition": {"nodes": [], "on_no_match": "", "entry_node_id": ""}, "revision": 0}

Errors

StatusCodeMessage
404not_foundWorkflow not found

Save the draft

PUT /workflows/{workflow_id}/draft

Replaces the draft definition. The save succeeds only if expected_revision equals the draft's current revision; the revision then goes up by one. This stops two editors from overwriting each other. The definition is stored as sent; it is not validated here.

Auth: API key or session token. Role: member and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.
expected_revisionbodyintegeryesThe revision you last read.
definitionbodyobjectyesSee The definition.

Request

bash
curl -X PUT https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/draft \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{
    "expected_revision": 0,
    "definition": {
      "entry_node_id": "n_start",
      "on_no_match": "n_end_unhandled",
      "nodes": [
        {"id": "n_start", "type": "start", "config": {"trigger": {"kind": "manual"}},
         "cases": [{"when": {"always": true}, "then": {"to": "n_call"}}]},
        {"id": "n_call", "type": "agent", "name": "Qualify",
         "config": {"agent_id": "f412f2b1-3493-4458-bcf2-8676cc798edd", "timeout_s": 3600,
                    "variables": {"name": {"value": {"var": "entry.name"}}}},
         "cases": [
           {"when": {"cmp": "==", "left": {"var": "call.status"}, "right": {"const": "completed"}},
            "then": {"to": "n_end_reached"}},
           {"when": {"cmp": "in", "left": {"var": "call.status"}, "right": {"const": ["busy", "no_answer"]}},
            "then": {"to": "n_retry"}}
         ]},
        {"id": "n_retry", "type": "retry",
         "config": {"target_node_id": "n_call", "attempts": [{"delay": {"minutes": 30}}, {"delay": {"hours": 2}}]},
         "cases": [{"when": {"always": true}, "then": {"to": "n_end_unreachable"}}]},
        {"id": "n_end_reached", "type": "end", "config": {"label": "reached", "outcome": "success"}},
        {"id": "n_end_unreachable", "type": "end", "config": {"label": "unreachable", "outcome": "failure"}},
        {"id": "n_end_unhandled", "type": "end", "config": {"label": "unhandled", "outcome": "failure"}}
      ]
    }
  }'

Response 200 OK

json
{"revision": 1}

Errors

StatusCodeMessage
404not_foundWorkflow not found
409revision_conflictexpected_revision 0 is stale; the draft is at revision 1 (with current_revision)
413definition_too_largeThe definition exceeds 512 KB
422validation_failedexpected_revision must be an integer >= 0
422validation_faileddefinition must be a JSON object

Validate the draft

POST /workflows/{workflow_id}/validate

Checks the saved draft and returns every problem found. Nothing is changed. The request needs no body.

Auth: API key or session token. Role: member and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.

Request

bash
curl -X POST https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/validate \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{"issues": []}

Each issue is an object with severity (error or warning), code, message and, where they apply, node_id and path. Only errors block publishing.

Errors

StatusCodeMessage
404not_foundWorkflow not found

Publish the draft

POST /workflows/{workflow_id}/publish

Validates the draft and freezes it as the next version. The draft's revision counter returns to 0.

Auth: API key or session token. Role: member and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.
expected_revisionbodyintegernoPublish only if the draft is still at this revision.

Request

bash
curl -X POST https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/publish \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision": 1}'

Response 200 OK

json
{"version": 1, "published_at": "2026-10-13T07:30:00Z", "warnings": []}

Errors

StatusCodeMessage
404not_foundWorkflow not found
409revision_conflictThe draft changed since you read it (with current_revision)
409already_publishedThe draft is identical to version <n> (with version)
422invalid_definitionThe draft has <n> error(s) (with the full issues list)

Get a published version

GET /workflows/{workflow_id}/versions/{version}

Returns the definition of a published version. It never changes once published.

Auth: API key or session token. Role: viewer and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.
versionpathintegeryesThe version number.

Request

bash
curl https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/versions/1 \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{
  "version": 1,
  "published_at": "2026-10-13T07:30:00Z",
  "definition": {"entry_node_id": "n_start", "on_no_match": "n_end_unhandled", "nodes": []},
  "warnings": []
}

definition.nodes holds the full node list; it is shortened here.

Errors

StatusCodeMessage
404not_foundWorkflow not found or Workflow version not found

Run a workflow for one contact

POST /workflows/{workflow_id}/run

Starts one execution of the latest published version for a single contact. The body is the contact: the system fields and the fields the start node declares. Declared fields may be sent at the top level or inside custom_fields.

Runs of one version are grouped under an automatic campaign of kind api, whose id is returned. A reference_id can run only once per version; if you leave it out, one is derived from the contact's values.

Auth: API key or session token. Role: member and up. When the operator sets REQUIRE_VERIFIED_EMAIL, the login must have a verified email address.

This route accepts an Idempotency-Key header; see Idempotency.

Warning: A run places real calls when an agent node is reached and the platform is connected to live telephony.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.
reference_idbodystringnoYour id for this contact.
mobile_numberbodystringnoE.164. The default recipient of agent nodes.
namebodystringnoContact name.
emailbodystringnoContact email.
declared fieldsbodyas declaredFields from the start node's config.fields.

Request

bash
curl -X POST https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/run \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: loan-1042-followup" \
  -d '{"reference_id": "loan-1042", "mobile_number": "+919876500012", "name": "Asha"}'

Response 201 Created

json
{
  "execution_id": "exec:7d6c5b4a-3928-4170-8695-a4b3c2d1e0f9:loan-1042",
  "campaign_id": "7d6c5b4a-3928-4170-8695-a4b3c2d1e0f9",
  "workflow_version": 1,
  "status": "pending"
}

Follow the run with Get a workflow execution.

Errors

StatusCodeMessage
404not_foundWorkflow not found
409duplicate_runreference_id <id> already ran on this version (with execution_id)
409invalid_stateRuns of this version were aborted
422no_published_versionThe workflow has no published version
422unknown_fieldUnknown field <name> (with fields)
422empty_rowThe row is empty
422missing_required_value<field>: a value is required
422invalid_phone<field>: expected an E.164 phone number, e.g. +919876543210
422invalid_field_type<field>: expected <type>

Send a test webhook

POST /workflows/{workflow_id}/webhook:test

Posts a sample execution.terminal payload to the workflow's webhook. The sample follows the first branch of each node of the latest published version from the start node to an end node, and is marked "test": true.

Auth: API key or session token. Role: admin and up.

ParameterInTypeRequiredDescription
workflow_idpathstring (UUID)yesThe workflow.

Request

bash
curl -X POST "https://api.sevennadi.si/workflows/2ab95da4-50dd-4492-a355-793ec7d810c5/webhook:test" \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{
  "webhook_url": "https://hooks.example.test/wf",
  "workflow_version": 1,
  "sample_path": ["n_start", "n_call", "n_end_reached"],
  "sample_outcome": "success",
  "result": {"delivered": true, "http_status": 200, "response_body": "ok"}
}

The call answers 200 even when delivery fails. Then result.delivered is false, with error_type (http_error or unreachable) and error.

Errors

StatusCodeMessage
403Standard body: Your role (member) can't change webhooks. It needs admin access; ask an owner or admin of the workspace.
404not_foundWorkflow not found
409no_webhookNo webhook is configured on this workflow
422no_published_versionThe workflow has no published version

List node types

GET /workflow-node-types

Returns the catalog of node types: each type's parameters with their types, defaults and bounds, what it branches on, the variable namespaces, and the available effects. Use it to build or check definitions.

Auth: API key or session token. Role: viewer and up.

No parameters.

Request

bash
curl https://api.sevennadi.si/workflow-node-types \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"

Response 200 OK

json
{
  "node_types": [
    {
      "type": "start",
      "label": "Start",
      "summary": "Entry point; declares the contact fields and input variables the workflow accepts.",
      "available": true,
      "transition": "cases",
      "branches_on": "entry.*",
      "params": [
        {"name": "trigger.kind", "type": "enum", "required": true, "description": "How contacts enter the workflow.", "options": ["manual"]},
        {"name": "fields", "type": "array", "required": false, "default": [], "description": "Declared contact fields (up to 50).", "max": 50}
      ]
    }
  ],
  "namespaces": ["call", "entry", "extraction", "node", "response", "state", "whatsapp"],
  "effects": [
    {
      "type": "set_value",
      "summary": "Writes an operand's value to state.<name> (one level deep) before moving on.",
      "example": {"set_value": {"state.attempted": {"const": true}}}
    }
  ]
}

node_types is shortened here to its first entry.