Skip to content

API Reference

Audience: Developers · Tags: api, reference

SnapLink is an early-access, lightweight integration-automation (iPaaS) platform. This is the complete reference for its REST API: Trigger → Mapping/Logic → Action flows, connectors, executions, and the AI-assisted Snap Studio flow builder.

Base URL: https://api.snaplink.cloudsolutionaccelerators.com/api/v1 for every endpoint below, except GET /health and GET /health/ready, which live at the service root with no /api/v1 prefix and no authentication.

Headers on every authenticated request:

Authorization: Bearer <token>
Content-Type: application/json

A token is issued by POST /auth/signup or POST /auth/login (see Auth). There is no separate API-key mechanism — the bearer token is the only credential the API accepts.


The envelope — read this before you write a client

Every SnapLink response body has a top-level data field on success. Most endpoint groups also wrap it with ok: true. Two groups do not.

Groups Success shape
/auth, /workspaces, /flows, /drafts, /connectors, /connections, /executions, /runs, /monitoring, /webhooks, /health, /health/ready { "ok": true, "data": { ... } }
/marketplace, /flow-designer (including every /flow-designer/studio/* route) { "data": { ... } } — no ok key at all

This is a real, current quirk of the API, not a typo in this document. /marketplace and /flow-designer predate the ok convention being applied consistently, and no ok: true was retrofitted onto their success responses.

Practical rule for every client you write: don't branch on response.ok (the JSON field) to decide whether a call succeeded — it won't be there on roughly a third of the API. Branch on the HTTP status code (2xx = success), then always read the payload from data. On any non-2xx status, the body is uniformly { "ok": false, "error": { "code", "message", "details"? } } regardless of which group the endpoint belongs to — error responses are consistent across the entire API even where success responses aren't.

// Success — /flows (has ok)
{ "ok": true, "data": { "flows": [] } }

// Success — /marketplace (no ok)
{ "data": { "connectors": [] } }

// Error — identical shape everywhere, always has ok:false
{ "ok": false, "error": { "code": "FLOW_NOT_FOUND", "message": "Flow not found." } }

Endpoint map

Tables below list every endpoint. Curl examples for the most-used ones follow each table. Snap Studio's five endpoints (/flow-designer/studio/*) are summarized here and covered in full depth, with worked examples, in Studio API. Webhook ingress (POST /webhooks/:flowId/:secret) is summarized here and covered in full depth in Webhooks.

Health (unversioned, no auth)

Method Path Purpose
GET /health Liveness probe
GET /health/ready Readiness probe (checks the database when running on Postgres)

curl https://api.snaplink.cloudsolutionaccelerators.com/health
{ "ok": true, "data": { "status": "ok", "service": "snaplink-backend", "version": "0.1.0", "uptimeSeconds": 41823, "timestamp": "2026-07-02T14:03:11.000Z" } }
curl https://api.snaplink.cloudsolutionaccelerators.com/health/ready
{ "ok": true, "data": { "ready": true, "driver": "postgres", "database": "ok" } }
If a configured Postgres database is unreachable, this returns HTTP 503 with { "ok": false, "error": { "code": "NOT_READY", "message": "Database is not reachable." } }. In memory driver mode (no database configured) it always returns { "ready": true, "driver": "memory", "database": "not-applicable" }.

Auth

Method Path Purpose
POST /auth/signup Create an account, returns a bearer token
POST /auth/login Log in, returns a bearer token
GET /auth/session Current user + workspace memberships (Bearer required)

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","password":"correct-horse-battery"}'
{ "ok": true, "data": { "token": "$TOKEN", "user": { "id": "usr_8c1f0a", "email": "jane@example.com" } } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","password":"correct-horse-battery"}'
{ "ok": true, "data": { "token": "$TOKEN", "user": { "id": "usr_8c1f0a", "email": "jane@example.com" } } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/auth/session -H "Authorization: Bearer $TOKEN"
{
  "ok": true,
  "data": {
    "user": { "id": "usr_8c1f0a", "email": "jane@example.com" },
    "workspaces": [
      { "workspace": { "id": "ws_51ac9d", "name": "Jane's Team" }, "role": "owner" }
    ]
  }
}

Signup fails 400 INVALID_EMAIL if the email has no @, 400 WEAK_PASSWORD if the password is under 8 characters, and 409 EMAIL_TAKEN if the account already exists. Login fails 401 INVALID_CREDENTIALS on any mismatch (no distinction between "no such user" and "wrong password").

Workspaces

Bearer required for every route below.

Method Path Purpose
GET /workspaces Workspaces the caller belongs to
POST /workspaces Create a workspace (caller becomes owner)
GET /workspaces/:id One workspace + caller's role
GET /workspaces/:id/members List members
POST /workspaces/:id/members Add a member (owner/admin only)
DELETE /workspaces/:id/members/:userId Remove a member (owner/admin only)

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/workspaces \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"Jane'"'"'s Team"}'
{ "ok": true, "data": { "workspace": { "id": "ws_51ac9d", "name": "Jane's Team", "ownerId": "usr_8c1f0a", "createdAt": "2026-07-02T14:00:00.000Z" }, "role": "owner" } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/workspaces -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "workspaces": [ { "workspace": { "id": "ws_51ac9d", "name": "Jane's Team", "ownerId": "usr_8c1f0a", "createdAt": "2026-07-02T14:00:00.000Z" }, "role": "owner" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/workspaces/ws_51ac9d -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "workspace": { "id": "ws_51ac9d", "name": "Jane's Team", "ownerId": "usr_8c1f0a", "createdAt": "2026-07-02T14:00:00.000Z" }, "role": "owner" } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/workspaces/ws_51ac9d/members -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "members": [ { "id": "mem_1a2b3c", "workspaceId": "ws_51ac9d", "userId": "usr_8c1f0a", "role": "owner", "createdAt": "2026-07-02T14:00:00.000Z" } ] } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/workspaces/ws_51ac9d/members \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"userId":"usr_44d1c8","role":"member"}'
{ "ok": true, "data": { "id": "mem_7e0b2f", "workspaceId": "ws_51ac9d", "userId": "usr_44d1c8", "role": "member", "createdAt": "2026-07-02T14:05:00.000Z" } }

curl -X DELETE https://api.snaplink.cloudsolutionaccelerators.com/api/v1/workspaces/ws_51ac9d/members/usr_44d1c8 \
  -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "removed": true } }

Adding or removing a member as a caller whose own role is member returns 403 FORBIDDEN. Any operation on a workspace the caller does not belong to — including one that doesn't exist — returns 404 WORKSPACE_NOT_FOUND, never 403 (no existence leak).

Flows & drafts

Bearer required; every route is workspace-scoped.

Method Path Purpose
GET /flows?workspaceId=&includeArchived= List flows
POST /flows Create a flow
GET /flows/:id Flow detail
GET /flows/:id/versions List a flow's published versions
POST /flows/:id/drafts Create a draft (definition)
GET /flows/:id/webhook Get-or-create the flow's webhook endpoint
POST /flows/:id/webhook/rotate Rotate the webhook secret
POST /flows/:id/archive Archive a flow
PUT /drafts/:id Update a draft's definition
POST /drafts/:id/promote Promote a draft to a new active version

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"workspaceId":"ws_51ac9d","name":"Lead alert","description":"Notify sales on new lead"}'
{
  "ok": true,
  "data": {
    "id": "flow_51ac9d", "workspaceId": "ws_51ac9d", "name": "Lead alert",
    "description": "Notify sales on new lead", "status": "draft",
    "activeVersionId": null, "createdBy": "usr_8c1f0a",
    "createdAt": "2026-07-02T14:10:00.000Z", "updatedAt": "2026-07-02T14:10:00.000Z", "archivedAt": null
  }
}

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows?workspaceId=ws_51ac9d&includeArchived=false" \
  -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "flows": [ { "id": "flow_51ac9d", "workspaceId": "ws_51ac9d", "name": "Lead alert", "status": "active", "activeVersionId": "flowver_9d2a1b", "description": "Notify sales on new lead", "createdBy": "usr_8c1f0a", "createdAt": "2026-07-02T14:10:00.000Z", "updatedAt": "2026-07-02T14:12:00.000Z", "archivedAt": null } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows/flow_51ac9d -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "flow_51ac9d", "workspaceId": "ws_51ac9d", "name": "Lead alert", "status": "active", "activeVersionId": "flowver_9d2a1b", "description": "Notify sales on new lead", "createdBy": "usr_8c1f0a", "createdAt": "2026-07-02T14:10:00.000Z", "updatedAt": "2026-07-02T14:12:00.000Z", "archivedAt": null } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows/flow_51ac9d/versions -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "versions": [ { "id": "flowver_9d2a1b", "flowId": "flow_51ac9d", "versionNumber": 1, "definition": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [] }, "checksum": "a1b2c3...", "createdBy": { "kind": "user", "id": "usr_8c1f0a" }, "createdAt": "2026-07-02T14:12:00.000Z" } ] } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows/flow_51ac9d/drafts \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"definition":{"trigger":{"key":"trigger","connectorKey":"webhook","triggerType":"webhook"},"actions":[{"key":"notify","connectorKey":"slack","actionType":"send_message","input":{"channel":"C0000000","text":"New lead!"}}]}}'
{ "ok": true, "data": { "id": "draft_7e0b2f", "flowId": "flow_51ac9d", "workspaceId": "ws_51ac9d", "definition": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [ { "key": "notify", "connectorKey": "slack", "actionType": "send_message", "input": { "channel": "C0000000", "text": "New lead!" } } ] }, "source": "human", "status": "proposed", "createdBy": { "kind": "user", "id": "usr_8c1f0a" }, "createdAt": "2026-07-02T14:13:00.000Z", "updatedAt": "2026-07-02T14:13:00.000Z" } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows/flow_51ac9d/webhook -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "flowId": "flow_51ac9d", "secret": "9f3a...48hex...c2", "path": "/api/v1/webhooks/flow_51ac9d/9f3a...48hex...c2" } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows/flow_51ac9d/webhook/rotate \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"graceSeconds":3600}'
{ "ok": true, "data": { "flowId": "flow_51ac9d", "secret": "b7e1...48hex...09", "secretVersion": 2, "path": "/api/v1/webhooks/flow_51ac9d/b7e1...48hex...09", "previousSecretExpiresAt": "2026-07-02T15:13:00.000Z" } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flows/flow_51ac9d/archive -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "flow_51ac9d", "workspaceId": "ws_51ac9d", "name": "Lead alert", "status": "archived", "activeVersionId": "flowver_9d2a1b", "description": "Notify sales on new lead", "createdBy": "usr_8c1f0a", "createdAt": "2026-07-02T14:10:00.000Z", "updatedAt": "2026-07-02T15:20:00.000Z", "archivedAt": "2026-07-02T15:20:00.000Z" } }

curl -X PUT https://api.snaplink.cloudsolutionaccelerators.com/api/v1/drafts/draft_7e0b2f \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"definition":{"trigger":{"key":"trigger","connectorKey":"webhook","triggerType":"webhook"},"actions":[{"key":"notify","connectorKey":"slack","actionType":"send_message","input":{"channel":"C0000000","text":"New lead — updated copy"}}]}}'
{ "ok": true, "data": { "id": "draft_7e0b2f", "flowId": "flow_51ac9d", "status": "proposed", "definition": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [ { "key": "notify", "connectorKey": "slack", "actionType": "send_message", "input": { "channel": "C0000000", "text": "New lead — updated copy" } } ] }, "workspaceId": "ws_51ac9d", "source": "human", "createdBy": { "kind": "user", "id": "usr_8c1f0a" }, "createdAt": "2026-07-02T14:13:00.000Z", "updatedAt": "2026-07-02T14:20:00.000Z" } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/drafts/draft_7e0b2f/promote -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "flow": { "id": "flow_51ac9d", "status": "active", "activeVersionId": "flowver_2b7c40", "workspaceId": "ws_51ac9d", "name": "Lead alert", "description": "Notify sales on new lead", "createdBy": "usr_8c1f0a", "createdAt": "2026-07-02T14:10:00.000Z", "updatedAt": "2026-07-02T14:21:00.000Z", "archivedAt": null }, "version": { "id": "flowver_2b7c40", "flowId": "flow_51ac9d", "versionNumber": 2, "definition": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [] }, "checksum": "d4e5f6...", "createdBy": { "kind": "user", "id": "usr_8c1f0a" }, "createdAt": "2026-07-02T14:21:00.000Z" } } }

Connectors & connections

Bearer required. The connector registry (/connectors) is global; connections (/connections) are workspace-scoped and never echo credential values back.

Method Path Purpose
GET /connectors List registered connector definitions
GET /connectors/:key One connector definition
GET /connectors/:key/versions Version history of a connector definition
GET /connections?workspaceId= List connections in a workspace
POST /connections Create a connection (optionally attach a credential)
GET /connections/:id One connection
PUT /connections/:id Rename a connection
DELETE /connections/:id Remove a connection
POST /connections/:id/validate Actively re-check connection health
GET /connections/:id/health Last known connection health

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connectors -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "connectors": [ { "id": "condef_1", "key": "slack", "version": "1.0.0", "displayName": "Slack", "authType": "oauth", "status": "active", "manifest": { "key": "slack", "version": "1.0.0", "displayName": "Slack", "category": "communication", "authType": "oauth", "capabilities": ["send_message"], "triggers": [], "actions": [] }, "createdAt": "2026-06-01T00:00:00.000Z", "updatedAt": "2026-06-01T00:00:00.000Z" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connectors/slack -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "condef_1", "key": "slack", "version": "1.0.0", "displayName": "Slack", "authType": "oauth", "status": "active", "manifest": { "key": "slack", "version": "1.0.0", "displayName": "Slack", "category": "communication", "authType": "oauth", "capabilities": ["send_message"], "triggers": [], "actions": [] }, "createdAt": "2026-06-01T00:00:00.000Z", "updatedAt": "2026-06-01T00:00:00.000Z" } }
A :key that no connector defines returns 404 CONNECTOR_NOT_FOUND.

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connectors/slack/versions -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "versions": [ { "id": "condef_1", "key": "slack", "version": "1.0.0", "displayName": "Slack", "authType": "oauth", "status": "active", "manifest": { "...": "..." }, "createdAt": "2026-06-01T00:00:00.000Z", "updatedAt": "2026-06-01T00:00:00.000Z" } ] } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"workspaceId":"ws_51ac9d","connectorDefinitionId":"condef_1","name":"#sales-alerts","credential":{"credentialType":"oauth_access","secret":"xoxb-REDACTED"}}'
{ "ok": true, "data": { "id": "conn_3a90ee", "workspaceId": "ws_51ac9d", "connectorDefinitionId": "condef_1", "name": "#sales-alerts", "status": "active", "credentialRef": "cred_ref_9f21", "createdAt": "2026-07-02T14:30:00.000Z", "updatedAt": "2026-07-02T14:30:00.000Z" } }
Note credentialRef is a non-secret pointer — the secret value you sent is never returned by any endpoint.

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "connections": [ { "id": "conn_3a90ee", "workspaceId": "ws_51ac9d", "connectorDefinitionId": "condef_1", "name": "#sales-alerts", "status": "active", "credentialRef": "cred_ref_9f21", "createdAt": "2026-07-02T14:30:00.000Z", "updatedAt": "2026-07-02T14:30:00.000Z" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections/conn_3a90ee -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "conn_3a90ee", "workspaceId": "ws_51ac9d", "connectorDefinitionId": "condef_1", "name": "#sales-alerts", "status": "active", "credentialRef": "cred_ref_9f21", "createdAt": "2026-07-02T14:30:00.000Z", "updatedAt": "2026-07-02T14:30:00.000Z" } }

curl -X PUT https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections/conn_3a90ee \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"#sales-alerts-renamed"}'
{ "ok": true, "data": { "id": "conn_3a90ee", "name": "#sales-alerts-renamed", "workspaceId": "ws_51ac9d", "connectorDefinitionId": "condef_1", "status": "active", "credentialRef": "cred_ref_9f21", "createdAt": "2026-07-02T14:30:00.000Z", "updatedAt": "2026-07-02T14:35:00.000Z" } }

curl -X DELETE https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections/conn_3a90ee -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "removed": true } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections/conn_3a90ee/validate -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "health_1", "connectionId": "conn_3a90ee", "status": "healthy", "lastCheckAt": "2026-07-02T14:36:00.000Z", "lastSuccessAt": "2026-07-02T14:36:00.000Z", "lastFailureAt": null, "failureReason": null } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/connections/conn_3a90ee/health -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "health_1", "connectionId": "conn_3a90ee", "status": "healthy", "lastCheckAt": "2026-07-02T14:36:00.000Z", "lastSuccessAt": "2026-07-02T14:36:00.000Z", "lastFailureAt": null, "failureReason": null } }

Executions

Bearer required. Creates and drives a single run of a flow immediately (inline, synchronous with respect to the HTTP call).

Method Path Purpose
POST /executions Run a flow now
GET /executions?workspaceId= List runs in a workspace
GET /executions/:id Run + steps + retries
GET /executions/:id/events Run's event log
GET /executions/:id/audit Run's audit log
POST /executions/:id/cancel Cancel a run
POST /executions/:id/retry Re-run as a new run

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"flowId":"flow_51ac9d","triggerType":"manual"}'
{ "ok": true, "data": { "id": "run_44d1c8", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowVersionId": "flowver_2b7c40", "status": "succeeded", "triggerType": "manual", "attempts": 1, "startedAt": "2026-07-02T14:40:00.000Z", "completedAt": "2026-07-02T14:40:01.000Z", "failureReason": null, "createdAt": "2026-07-02T14:40:00.000Z" } }
triggerType defaults to manual when omitted; it accepts manual, webhook, schedule, or test.

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "runs": [ { "id": "run_44d1c8", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowVersionId": "flowver_2b7c40", "status": "succeeded", "triggerType": "manual", "attempts": 1, "startedAt": "2026-07-02T14:40:00.000Z", "completedAt": "2026-07-02T14:40:01.000Z", "failureReason": null, "createdAt": "2026-07-02T14:40:00.000Z" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions/run_44d1c8 -H "Authorization: Bearer $TOKEN"
{
  "ok": true,
  "data": {
    "run": { "id": "run_44d1c8", "status": "succeeded", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowVersionId": "flowver_2b7c40", "triggerType": "manual", "attempts": 1, "startedAt": "2026-07-02T14:40:00.000Z", "completedAt": "2026-07-02T14:40:01.000Z", "failureReason": null, "createdAt": "2026-07-02T14:40:00.000Z" },
    "steps": [ { "id": "step_1", "executionRunId": "run_44d1c8", "stepNumber": 1, "stepType": "action", "connectorKey": "slack", "actionType": "send_message", "status": "succeeded", "attempt": 1, "startedAt": "2026-07-02T14:40:00.400Z", "completedAt": "2026-07-02T14:40:00.900Z", "errorCode": null, "failureReason": null } ],
    "retries": []
  }
}

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions/run_44d1c8/events -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "events": [ { "id": "evt_1", "executionRunId": "run_44d1c8", "workspaceId": "ws_51ac9d", "eventType": "run.started", "payload": {}, "createdAt": "2026-07-02T14:40:00.000Z" }, { "id": "evt_2", "executionRunId": "run_44d1c8", "workspaceId": "ws_51ac9d", "eventType": "run.succeeded", "payload": {}, "createdAt": "2026-07-02T14:40:01.000Z" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions/run_44d1c8/audit -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "audit": [ { "id": "aud_1", "executionRunId": "run_44d1c8", "workspaceId": "ws_51ac9d", "auditType": "run.created", "details": { "triggerType": "manual" }, "createdAt": "2026-07-02T14:40:00.000Z" } ] } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions/run_44d1c8/cancel -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "run_44d1c8", "status": "cancelled", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowVersionId": "flowver_2b7c40", "triggerType": "manual", "attempts": 1, "startedAt": "2026-07-02T14:40:00.000Z", "completedAt": "2026-07-02T14:40:02.000Z", "failureReason": null, "createdAt": "2026-07-02T14:40:00.000Z" } }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/executions/run_44d1c8/retry -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "id": "run_9a02f1", "status": "succeeded", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowVersionId": "flowver_2b7c40", "triggerType": "manual", "attempts": 1, "startedAt": "2026-07-02T14:45:00.000Z", "completedAt": "2026-07-02T14:45:01.000Z", "failureReason": null, "createdAt": "2026-07-02T14:45:00.000Z" } }
Retry always creates a new run (run_9a02f1 above, distinct from run_44d1c8) — it never mutates the original.

Runs (read-only history)

Bearer required. /runs is the paginated, filterable read model over the same underlying data /executions writes; use it for history views and dashboards.

Method Path Purpose
GET /runs?workspaceId=&flowId=&status=&from=&to=&connector=&errorCode=&limit=&offset= Paginated, filtered run history
GET /runs/:id Run detail (run + steps + retries + REST/webhook invocations)
GET /runs/:id/timeline Reconstructed chronological timeline
GET /runs/:id/events Run's event log
GET /runs/:id/audit Run's audit log
GET /runs/:id/retries Run's retry attempts

See Pagination and Filtering below for the query parameters. Full worked example:

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs?workspaceId=ws_51ac9d&status=failed&limit=20&offset=0" \
  -H "Authorization: Bearer $TOKEN"
{
  "ok": true,
  "data": {
    "items": [
      {
        "runId": "run_2f88ab", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowName": "Lead alert",
        "status": "failed", "triggerType": "webhook",
        "startedAt": "2026-07-02T13:10:00.000Z", "completedAt": "2026-07-02T13:10:02.000Z",
        "durationMs": 2000, "retryCount": 1, "stepCount": 1, "createdAt": "2026-07-02T13:10:00.000Z"
      }
    ],
    "total": 1,
    "limit": 20,
    "offset": 0
  }
}

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs/run_2f88ab -H "Authorization: Bearer $TOKEN"
{
  "ok": true,
  "data": {
    "run": { "id": "run_2f88ab", "status": "failed", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowVersionId": "flowver_2b7c40", "triggerType": "webhook", "attempts": 2, "startedAt": "2026-07-02T13:10:00.000Z", "completedAt": "2026-07-02T13:10:02.000Z", "failureReason": "slack.send_message: auth", "createdAt": "2026-07-02T13:10:00.000Z" },
    "steps": [ { "id": "step_9", "executionRunId": "run_2f88ab", "stepNumber": 1, "stepType": "action", "connectorKey": "slack", "actionType": "send_message", "status": "failed", "attempt": 2, "startedAt": "2026-07-02T13:10:01.500Z", "completedAt": "2026-07-02T13:10:02.000Z", "errorCode": "auth", "failureReason": "Invalid or expired token" } ],
    "retries": [ { "id": "retry_1", "executionRunId": "run_2f88ab", "executionStepId": "step_9", "attemptNumber": 2, "strategy": "exponential", "notBefore": "2026-07-02T13:10:01.000Z", "startedAt": "2026-07-02T13:10:01.500Z", "completedAt": "2026-07-02T13:10:02.000Z", "status": "failed" } ],
    "restInvocations": [],
    "webhookInvocations": [ { "id": "wh_1", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "endpointId": "endpoint_1", "method": "POST", "contentType": "application/json", "requestSizeBytes": 214, "status": "accepted", "rejectionReason": null, "executionRunId": "run_2f88ab", "createdAt": "2026-07-02T13:10:00.000Z" } ]
  }
}

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs/run_2f88ab/timeline -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "timeline": [ { "at": "2026-07-02T13:10:00.000Z", "kind": "event", "type": "run.started", "label": "Run started", "detail": {} }, { "at": "2026-07-02T13:10:02.000Z", "kind": "step", "type": "step.failed", "label": "slack.send_message failed", "detail": { "errorCode": "auth" } } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs/run_2f88ab/events -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "events": [ { "id": "evt_9", "executionRunId": "run_2f88ab", "workspaceId": "ws_51ac9d", "eventType": "run.failed", "payload": {}, "createdAt": "2026-07-02T13:10:02.000Z" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs/run_2f88ab/audit -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "audit": [ { "id": "aud_9", "executionRunId": "run_2f88ab", "workspaceId": "ws_51ac9d", "auditType": "run.failed", "details": { "errorCode": "auth" }, "createdAt": "2026-07-02T13:10:02.000Z" } ] } }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs/run_2f88ab/retries -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "retries": [ { "id": "retry_1", "executionRunId": "run_2f88ab", "executionStepId": "step_9", "attemptNumber": 2, "strategy": "exponential", "notBefore": "2026-07-02T13:10:01.000Z", "startedAt": "2026-07-02T13:10:01.500Z", "completedAt": "2026-07-02T13:10:02.000Z", "status": "failed" } ] } }

GET /runs returns 400 VALIDATION_ERROR if status is present but isn't one of queued, running, succeeded, failed, cancelled, retry_pending, retry_running, retry_exhausted.

Monitoring

Bearer required, workspace-scoped via ?workspaceId=. Every route accepts the same filter query params as /runs (flowId, status, from, to, connector, errorCode) where relevant.

Method Path Purpose
GET /monitoring/summary Aggregate run counts + success/failure/retry rates + duration stats
GET /monitoring/failures Failures grouped by error code
GET /monitoring/retries Retry metrics
GET /monitoring/durations Duration distribution (avg/min/max/p95)
GET /monitoring/flows Per-flow run stats
GET /monitoring/webhooks Webhook invocation log (accepted + rejected)
GET /monitoring/rest REST action invocation log

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/summary?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{
  "ok": true,
  "data": {
    "workspaceId": "ws_51ac9d", "totalRuns": 128, "successfulRuns": 119, "failedRuns": 6,
    "retryExhaustedRuns": 1, "cancelledRuns": 2, "inProgressRuns": 0, "retriedRuns": 8,
    "successRate": 0.93, "failureRate": 0.05, "retryRate": 0.06,
    "duration": { "count": 128, "avgMs": 340, "minMs": 90, "maxMs": 2100, "p95Ms": 900, "smallSample": false }
  }
}

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/failures?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "failures": [ { "errorCode": "auth", "occurrences": 4, "affectedFlows": 1, "firstSeen": "2026-06-20T10:00:00.000Z", "lastSeen": "2026-07-02T13:10:02.000Z" } ] } }

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/retries?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "count": 8, "avgMs": 1500, "minMs": 500, "maxMs": 4200, "p95Ms": 3800, "smallSample": false } }

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/durations?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "count": 128, "avgMs": 340, "minMs": 90, "maxMs": 2100, "p95Ms": 900, "smallSample": false } }

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/flows?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": [ { "flowId": "flow_51ac9d", "flowName": "Lead alert", "totalRuns": 128, "successfulRuns": 119, "failedRuns": 6, "retriedRuns": 8, "successRate": 0.93, "retryRate": 0.06 } ] }

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/webhooks?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "invocations": [ { "id": "wh_1", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "endpointId": "endpoint_1", "method": "POST", "contentType": "application/json", "requestSizeBytes": 214, "status": "accepted", "rejectionReason": null, "executionRunId": "run_2f88ab", "createdAt": "2026-07-02T13:10:00.000Z" } ] } }

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/monitoring/rest?workspaceId=ws_51ac9d" -H "Authorization: Bearer $TOKEN"
{ "ok": true, "data": { "invocations": [ { "id": "rest_1", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "url": "https://api.example.com/status", "method": "GET", "statusCode": 200, "createdAt": "2026-07-02T13:12:00.000Z" } ] } }

Marketplace

Bearer required. Envelope is { "data": ... } only — no ok key on success (see The envelope). Browse/search/analytics routes are session-only; install and lifecycle routes are workspace-scoped and require membership.

Method Path Purpose
GET /marketplace/connectors?q= Browse (or search, if q given) all connectors
GET /marketplace/categories Categories with connector counts
GET /marketplace/search?q= Free-text search
GET /marketplace/connectors/:key One connector's marketplace listing
POST /marketplace/installed Install a connector into a workspace
GET /marketplace/installed/:workspaceId List a workspace's installed connectors
POST /marketplace/installed/transition Advance a connector's lifecycle state
GET /marketplace/analytics Usage analytics for every connector
GET /marketplace/analytics/:key Usage analytics for one connector

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/connectors?q=slack" -H "Authorization: Bearer $TOKEN"
{ "data": [ { "key": "slack", "displayName": "Slack", "vendor": "Slack Technologies", "category": "communication", "authType": "oauth", "logoUrl": null, "docsUrl": null, "version": "1.0.0", "status": "available", "capabilities": ["send_message"], "capabilityDetail": ["send_message"] } ] }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/categories -H "Authorization: Bearer $TOKEN"
{ "data": [ { "category": "communication", "count": 2 }, { "category": "crm", "count": 1 } ] }

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/search?q=hub" -H "Authorization: Bearer $TOKEN"
{ "data": [ { "key": "hubspot", "displayName": "HubSpot", "vendor": "HubSpot", "category": "crm", "authType": "oauth", "logoUrl": null, "docsUrl": null, "version": "1.0.0", "status": "available", "capabilities": ["create_contact"], "capabilityDetail": ["create_contact"] } ] }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/connectors/slack -H "Authorization: Bearer $TOKEN"
{ "data": { "key": "slack", "displayName": "Slack", "vendor": "Slack Technologies", "category": "communication", "authType": "oauth", "logoUrl": null, "docsUrl": null, "version": "1.0.0", "status": "available", "capabilities": ["send_message"], "capabilityDetail": ["send_message"] } }
A :key with no listing returns 404 CONNECTOR_NOT_FOUND.

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/installed \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"workspaceId":"ws_51ac9d","connectorKey":"slack"}'
{ "data": { "id": "inst_1a2b", "workspaceId": "ws_51ac9d", "connectorKey": "slack", "state": "installed", "installedAt": "2026-07-02T14:50:00.000Z", "updatedAt": "2026-07-02T14:50:00.000Z" } }
Installing the same connector twice returns 409 ALREADY_INSTALLED.

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/installed/ws_51ac9d -H "Authorization: Bearer $TOKEN"
{ "data": [ { "id": "inst_1a2b", "workspaceId": "ws_51ac9d", "connectorKey": "slack", "state": "installed", "installedAt": "2026-07-02T14:50:00.000Z", "updatedAt": "2026-07-02T14:50:00.000Z" } ] }

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/installed/transition \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"workspaceId":"ws_51ac9d","connectorKey":"slack","action":"activate"}'
{ "data": { "id": "inst_1a2b", "workspaceId": "ws_51ac9d", "connectorKey": "slack", "state": "active", "installedAt": "2026-07-02T14:50:00.000Z", "updatedAt": "2026-07-02T14:52:00.000Z" } }
action is one of configure, certify, activate, disable, deprecate; each maps to a lifecycle-state transition (configured, certified, active, disabled, deprecated). Transitioning a connector that was never installed returns 404 NOT_INSTALLED.

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/analytics -H "Authorization: Bearer $TOKEN"
{ "data": [ { "connector": "slack", "installs": 1, "runs": 128, "successes": 119, "failures": 9, "successRate": 0.93, "failureRate": 0.07, "latencyMs": 210, "topActions": ["send_message"] } ] }

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/marketplace/analytics/slack -H "Authorization: Bearer $TOKEN"
{ "data": { "connector": "slack", "installs": 1, "runs": 128, "successes": 119, "failures": 9, "successRate": 0.93, "failureRate": 0.07, "latencyMs": 210, "topActions": ["send_message"] } }

Flow designer

Bearer required. Envelope is { "data": ... } only — no ok key on success, same as Marketplace.

Method Path Purpose
GET /flow-designer/templates?category= List (or filter) the 7 built-in flow templates
GET /flow-designer/templates/:id One template
POST /flow-designer/draft Legacy rule-based drafter — prompt → draft flow (always draft-only)
POST /flow-designer/validate Design-time validation of a flow definition
POST /flow-designer/explain Plain-English explanation of a flow definition
POST /flow-designer/snap Snap Pad: one-click real send (REST POST or email)
GET /flow-designer/debug/:runId Read-only debug report for a run
POST /flow-designer/studio/plan Snap Studio — prompt → editable plan
POST /flow-designer/studio/check Snap Studio — validate a (possibly edited) plan
POST /flow-designer/studio/approve Snap Studio — save an approved plan as an active flow
POST /flow-designer/studio/test-run Snap Studio — run the saved flow and explain the result
GET /flow-designer/studio/runs/:runId/explain Snap Studio — re-fetch a run's plain-English explanation

The five studio/* routes are Snap Studio, SnapLink's guided AI flow builder. They're covered in full — request/response bodies, the plan schema, error shapes, and an end-to-end scripted walkthrough — in Studio API.

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/templates -H "Authorization: Bearer $TOKEN"
{
  "data": [
    { "id": "webhook-to-slack", "title": "Webhook → Slack Message", "description": "When a webhook fires, post a message to a Slack channel.", "category": "notifications", "requiredConnectors": ["slack"], "flowDefinition": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [ { "key": "notify", "connectorKey": "slack", "actionType": "send_message", "input": { "channel": "C0000000", "text": "New event received" } } ] } }
  ]
}
The full set of 7 templates: webhook-to-slack, webhook-to-teams, webhook-to-hubspot, hubspot-lead-to-slack, hubspot-to-teams, rest-to-slack, rest-to-teams — spanning categories notifications, crm, and sales.

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/templates/webhook-to-slack -H "Authorization: Bearer $TOKEN"
{ "data": { "id": "webhook-to-slack", "title": "Webhook → Slack Message", "description": "When a webhook fires, post a message to a Slack channel.", "category": "notifications", "requiredConnectors": ["slack"], "flowDefinition": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [ { "key": "notify", "connectorKey": "slack", "actionType": "send_message", "input": { "channel": "C0000000", "text": "New event received" } } ] } } }
An unknown :id returns 404 TEMPLATE_NOT_FOUND.

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/draft \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"prompt":"When a webhook fires, post to Slack"}'
{
  "data": {
    "draftFlow": { "trigger": { "key": "trigger", "connectorKey": "webhook", "triggerType": "webhook" }, "actions": [ { "key": "notify", "connectorKey": "slack", "actionType": "send_message", "input": { "channel": "C0000000", "text": "New event received" } } ] },
    "rationale": "Matched the 'webhook-to-slack' template based on keywords in your prompt.",
    "matchedTemplateId": "webhook-to-slack",
    "explanation": "When a webhook trigger fires, this flow will Slack: send message.",
    "activated": false
  }
}
activated is always false — this endpoint (and Snap Studio) never activates a flow; you must separately promote/approve it. This is the legacy rule-based drafter — Snap Studio (/flow-designer/studio/*) is the current, richer flow-authoring surface.

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/validate \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"workspaceId":"ws_51ac9d","definition":{"trigger":{"key":"trigger","connectorKey":"webhook","triggerType":"webhook"},"actions":[{"key":"notify","connectorKey":"slack","actionType":"send_message","input":{}}]}}'
{ "data": { "valid": false, "errors": [ { "code": "schema_mismatch", "message": "Missing required input 'channel' for 'send_message'.", "location": "notify" } ] } }
errors[].code is one of invalid_structure, unsupported_trigger, missing_connector, missing_capability, missing_credential, schema_mismatch, duplicate_key, orphan_node, cycle — a design-time taxonomy distinct from the top-level error.code values in Error responses.

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/explain \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"definition":{"trigger":{"key":"trigger","connectorKey":"webhook","triggerType":"webhook"},"actions":[{"key":"notify","connectorKey":"slack","actionType":"send_message","input":{"channel":"C0000000","text":"hi"}}]}}'
{
  "data": {
    "summary": "When a webhook trigger fires, this flow will slack: send message.",
    "executionPath": ["When a webhook trigger fires", "Slack: send message"],
    "connectorsUsed": ["slack"],
    "requiredCredentials": ["Slack (oauth)"],
    "potentialFailurePoints": ["Slack send message could fail (auth, rate limit, or upstream error) — the engine will retry per policy."]
  }
}

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/snap \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"text":"Ping from SnapLink","destination":"https://webhook.site/abc-123","title":"Test ping"}'
{ "data": { "runId": "run_c02f19", "status": "succeeded", "flowId": "flow_snap01", "workspaceId": "ws_51ac9d", "destination": "https://webhook.site/abc-123", "channel": "rest" } }
destination is either an http(s):// URL (→ channel: "rest", delivered as a REST POST) or an email address (→ channel: "email"). This is a real, immediate send through the execution engine — not a simulation — reusing a single internal "Snap Pad" flow per workspace. Empty text returns 400 EMPTY_SNAP; a destination that's neither a URL nor an email returns 400 INVALID_DESTINATION.

curl https://api.snaplink.cloudsolutionaccelerators.com/api/v1/flow-designer/debug/run_c02f19 -H "Authorization: Bearer $TOKEN"
{
  "data": {
    "runId": "run_c02f19", "status": "succeeded", "attempts": 1,
    "timeline": [ { "at": "2026-07-02T15:00:00.000Z", "kind": "event", "type": "run.started", "label": "Run started", "detail": {} } ],
    "steps": [ { "id": "step_1", "executionRunId": "run_c02f19", "stepNumber": 1, "stepType": "action", "connectorKey": "rest_sdk", "actionType": "request", "status": "succeeded", "attempt": 1, "startedAt": "2026-07-02T15:00:00.100Z", "completedAt": "2026-07-02T15:00:00.400Z", "errorCode": null, "failureReason": null } ],
    "retries": [],
    "connectorInvocations": [ { "stepNumber": 1, "connectorKey": "rest_sdk", "capability": "request", "status": "succeeded", "attempt": 1, "errorCode": null } ],
    "failureCause": null,
    "suggestedFix": null
  }
}
This is read-only — it never affects the run. Debug reports are available for any run in your workspace, not only Snap Pad runs.

Webhooks (public ingress, no auth)

Method Path Purpose
POST /webhooks/:flowId/:secret Trigger a flow — the secret in the URL path IS the authorization

curl -X POST https://api.snaplink.cloudsolutionaccelerators.com/api/v1/webhooks/flow_51ac9d/9f3a...48hex...c2 \
  -H "Content-Type: application/json" -d '{"anything":"you want"}'
{ "ok": true, "data": { "runId": "run_7c11de", "status": "queued" } }

Full detail — getting your URL, secret rotation and grace windows, the 1MB body cap, the no-storage policy, and rate limiting — is in Webhooks.


Request / response worked example

A typical read: paginated run history for a workspace.

curl "https://api.snaplink.cloudsolutionaccelerators.com/api/v1/runs?workspaceId=ws_51ac9d&limit=2&offset=0" \
  -H "Authorization: Bearer $TOKEN"
{
  "ok": true,
  "data": {
    "items": [
      { "runId": "run_9a02f1", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowName": "Lead alert", "status": "succeeded", "triggerType": "manual", "startedAt": "2026-07-02T14:45:00.000Z", "completedAt": "2026-07-02T14:45:01.000Z", "durationMs": 1000, "retryCount": 0, "stepCount": 1, "createdAt": "2026-07-02T14:45:00.000Z" },
      { "runId": "run_44d1c8", "workspaceId": "ws_51ac9d", "flowId": "flow_51ac9d", "flowName": "Lead alert", "status": "succeeded", "triggerType": "manual", "startedAt": "2026-07-02T14:40:00.000Z", "completedAt": "2026-07-02T14:40:01.000Z", "durationMs": 1000, "retryCount": 0, "stepCount": 1, "createdAt": "2026-07-02T14:40:00.000Z" }
    ],
    "total": 42,
    "limit": 2,
    "offset": 0
  }
}
Fetch the next page with offset=2; stop once items.length < limit or offset + items.length >= total.

Pagination

GET /runs is the only cursor-free, offset-paginated list endpoint. It accepts:

  • limit — page size. Defaults to 20, capped at 100. Non-numeric or out-of-range values are clamped, not rejected.
  • offset — number of items to skip. Defaults to 0.

The response always includes total (the full matching count, independent of the page) alongside items, limit, and offset. There is no cursor/token-based pagination — offset/limit is the only mechanism the API supports.

Every other list endpoint (/flows, /connections, /marketplace/connectors, etc.) returns its full result set in one response; none of them currently paginate.

Filtering

GET /runs and every /monitoring/* route accept the same filter query parameters:

Param Type Notes
workspaceId string Required
flowId string Optional
status one of queued, running, succeeded, failed, cancelled, retry_pending, retry_running, retry_exhausted Optional; 400 VALIDATION_ERROR if it's anything else
from / to ISO 8601 timestamps Optional; bounds on createdAt
connector string Optional; matches runs whose steps invoked this connector key
errorCode string Optional; matches runs whose steps failed with this code (see the taxonomy in Error responses)

/marketplace/connectors, /marketplace/search accept a single q free-text query param.

Rate limits

Webhook ingress (POST /webhooks/:flowId/:secret) is rate-limited per IP, per endpoint, and per workspace; a tripped limit returns 429 RATE_LIMITED with error.details.retryAfterSeconds. See Rate limiting for limits and backoff guidance, and Webhooks for the full ingress contract.

Error responses

Every error response, from every endpoint with no exception, has this exact shape:

{ "ok": false, "error": { "code": "FLOW_NOT_FOUND", "message": "Flow not found.", "details": null } }

details is optional and varies by code — often zod validation issues for VALIDATION_ERROR, or { retryAfterSeconds } for RATE_LIMITED.

HTTP error.code Where
400 VALIDATION_ERROR Any endpoint — bad request body/query
400 INVALID_EMAIL POST /auth/signup
400 WEAK_PASSWORD POST /auth/signup — password under 8 characters
400 STUDIO_PLAN_MALFORMED Studio check/approve — plan fails schema/whitelist validation
400 STUDIO_PLAN_INVALID Studio approve — plan has unresolved problems; carries error.details.problems
401 UNAUTHENTICATED Any Bearer-protected endpoint — missing/invalid token
401 INVALID_CREDENTIALS POST /auth/login — bad email/password
403 FORBIDDEN Workspace member routes — caller's role is insufficient
404 WORKSPACE_NOT_FOUND Any workspace-scoped route — no such workspace, or caller isn't a member
404 FLOW_NOT_FOUND Flow routes
404 DRAFT_NOT_FOUND Draft routes
404 RUN_NOT_FOUND Execution/run routes
404 STEP_NOT_FOUND Execution step lookups
404 RETRY_NOT_FOUND Retry attempt lookups
404 CONNECTOR_NOT_FOUND Connector registry / marketplace routes
404 CONNECTION_NOT_FOUND Connection routes
404 HEALTH_NOT_FOUND Connection health routes
404 TEMPLATE_NOT_FOUND Flow template routes
409 EMAIL_TAKEN POST /auth/signup — duplicate account
429 RATE_LIMITED Webhook ingress — carries error.details.retryAfterSeconds
503 NOT_READY GET /health/ready only

Cross-workspace access — a real ID that exists but belongs to a workspace you're not a member of — returns the resource's ordinary 404 *_NOT_FOUND code, never 403. This is deliberate: it avoids confirming that a resource exists to a caller who shouldn't be able to see it.

A few endpoints throw additional, narrowly-scoped codes worth knowing if you integrate against them directly: webhook ingress returns 404 WEBHOOK_NOT_FOUND (not FLOW_NOT_FOUND) for an unknown flow or bad secret, and 409 FLOW_NOT_ACTIVE if the target flow isn't active (see Webhooks); POST /flow-designer/snap returns 400 EMPTY_SNAP / 400 INVALID_DESTINATION; marketplace install/lifecycle routes return 409 ALREADY_INSTALLED, 404 NOT_INSTALLED, 409 NOT_CERTIFIABLE, and 422 CERTIFICATION_FAILED. Requests to routes that don't exist at all get a generic 404 NOT_FOUND; truly unexpected server errors get 500 INTERNAL_ERROR.

Run/step failure taxonomy. Separately from the HTTP error.code above, a failed execution step carries an errorCode drawn from a fixed connector-failure taxonomy: auth, rate_limit, timeout, upstream_5xx, bad_request, blocked, unknown. This is the same value GET /runs?errorCode= filters on, and what GET /flow-designer/debug/:runId uses to suggest a fix.


SDK examples (login → list runs)

Every example below reads the token/data the same way regardless of which envelope shape the endpoint uses: check the HTTP status for success, then read .data (or ["data"]) directly — never assume an ok field is present in the body.

cURL

BASE="https://api.snaplink.cloudsolutionaccelerators.com/api/v1"
TOKEN=$(curl -s -X POST "$BASE/auth/login" -H 'Content-Type: application/json' \
  -d '{"email":"jane@example.com","password":"correct-horse-battery"}' | jq -r .data.token)
curl -s "$BASE/runs?workspaceId=$WS" -H "Authorization: Bearer $TOKEN" | jq .data

JavaScript / TypeScript

const BASE = "https://api.snaplink.cloudsolutionaccelerators.com/api/v1";

async function login(email: string, password: string) {
  const res = await fetch(`${BASE}/auth/login`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ email, password }),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(body.error.message); // error shape is always { ok:false, error }
  return body.data.token as string; // success shape here has ok:true, but we only read .data
}

async function listRuns(token: string, workspaceId: string) {
  const res = await fetch(`${BASE}/runs?workspaceId=${workspaceId}`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  const body = await res.json();
  if (!res.ok) throw new Error(body.error.message);
  return body.data; // { items, total, limit, offset }
}

const token = await login("jane@example.com", "correct-horse-battery");
const runs = await listRuns(token, "ws_51ac9d");

Python

import requests

BASE = "https://api.snaplink.cloudsolutionaccelerators.com/api/v1"

def login(email, password):
    res = requests.post(f"{BASE}/auth/login", json={"email": email, "password": password})
    body = res.json()
    if not res.ok:
        raise RuntimeError(body["error"]["message"])
    return body["data"]["token"]

def list_runs(token, workspace_id):
    res = requests.get(f"{BASE}/runs", params={"workspaceId": workspace_id},
                        headers={"Authorization": f"Bearer {token}"})
    body = res.json()
    if not res.ok:
        raise RuntimeError(body["error"]["message"])
    return body["data"]

token = login("jane@example.com", "correct-horse-battery")
runs = list_runs(token, "ws_51ac9d")

C#

using var http = new HttpClient();
var loginRes = await http.PostAsJsonAsync($"{BASE}/auth/login", new { email, password });
var loginBody = await loginRes.Content.ReadFromJsonAsync<JsonElement>();
if (!loginRes.IsSuccessStatusCode)
    throw new Exception(loginBody.GetProperty("error").GetProperty("message").GetString());
var token = loginBody.GetProperty("data").GetProperty("token").GetString();

http.DefaultRequestHeaders.Authorization = new("Bearer", token);
var runsRes = await http.GetAsync($"{BASE}/runs?workspaceId={ws}");
var runsBody = await runsRes.Content.ReadFromJsonAsync<JsonElement>();
if (!runsRes.IsSuccessStatusCode)
    throw new Exception(runsBody.GetProperty("error").GetProperty("message").GetString());
var data = runsBody.GetProperty("data");

PowerShell

$loginRes = Invoke-RestMethod "$BASE/auth/login" -Method Post -ContentType 'application/json' `
  -Body (@{ email = $Email; password = $Password } | ConvertTo-Json) -SkipHttpErrorCheck -StatusCodeVariable loginStatus
if ($loginStatus -ge 400) { throw $loginRes.error.message }
$token = $loginRes.data.token

$runsRes = Invoke-RestMethod "$BASE/runs?workspaceId=$Ws" -Headers @{ Authorization = "Bearer $token" } `
  -SkipHttpErrorCheck -StatusCodeVariable runsStatus
if ($runsStatus -ge 400) { throw $runsRes.error.message }
$data = $runsRes.data

Postman / collection guide

Import the endpoints above as a collection: set a baseUrl variable, a token variable populated from POST /auth/login's data.token, and an Authorization: Bearer {{token}} header at the collection level. An exportable OpenAPI spec is Planned — none exists yet, so don't link to one.


Related: Studio API · Webhooks · Rate limiting · Creating flows