Skip to content
SBDocs

Customers · API reference

Runs

Start a run of a workflow or an agent, then read its status and output — by polling, or by a webhook when it finishes.

POSTRun a workflow#

POST https://app.sushibelt.ai/api/v1/workflows/{id}/runs

Starts a run of one workflow. The cost is estimated and held from your team's credits before anything runs. Use the returned jobId to read the run, or pass callbackUrl to be told when it finishes. A workflow can have one run in progress at a time.

Needs an API key with the runs:create scope. See Scopes.

Parameters#

Run a workflow: parameters
NameDescription
idstring (uuid) · in path · RequiredThe workflow to run.

Request body#

Every field is optional. An empty or missing body runs the workflow as it was built.

Run a workflow: request body fields
NameDescription
inputsobject (key → string) · OptionalValues that replace a step's value for this run, keyed by the step's API input key. A key that matches no step is refused with 422.
modestring · Optionaldraft runs a cheaper pass for trying a workflow out: one variation, fewer sampling steps, short text answers. It is not a discount — a model priced per run costs the same. preview behaves like production. One of: production, draft, preview.Default: "production"
deferboolean · OptionalQueue the run to start later instead of now. The answer is 202 with a deferredJobId. A deferred run that has not started within 24 hours expires. Follow it with Get a deferred run; a callbackUrl passed with it is kept for the run it becomes.Default: false
maxMultipliernumber · OptionalUsed only with defer: the price multiplier the deferred run waits for.Default: 0.7
callbackUrlstring (uri) · OptionalAn https URL to send the Run finished webhook to. Anything other than https is refused with 422.
callbackSecretstring · OptionalA secret of your choosing. When set, each webhook is signed with it in X-Sushibelt-Signature.

Example request#

Set SUSHIBELT_API_KEY to your key first, and replace each YOUR_… value.

Run a workflow: request
WORKFLOW_ID="YOUR_WORKFLOW_ID"

curl -X POST "https://app.sushibelt.ai/api/v1/workflows/$WORKFLOW_ID/runs" \
  -H "Authorization: Bearer $SUSHIBELT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {
      "subject": "a red fox in snow"
    }
  }'

Responses#

  • 200 OK — The run started.
  • 202 Accepted — With defer: true: the run is queued to start later.
  • 401 Unauthorized — The key is missing, malformed, unknown, revoked or expired. Message: Unauthorized.
  • 402 Payment required — The run would spend more than you allow: the team's balance is below the estimate, the project's hard cap would be passed, one step would pass the team's per-step credit limit, or this key has reached its monthly credit cap. Messages: Insufficient credits, Monthly credit cap reached.
  • 403 Forbidden — The key does not have the runs:create scope, or it is limited to other workflows. Messages: Scope not permitted for this key, Workflow not permitted for this key.
  • 404 Not found — Nothing with this id exists in your team, or the workflow has no canvas. Messages: Workflow not found, Canvas not found.
  • 409 Conflict — This workflow already has a run in progress.
  • 422 Unprocessable content — An inputs key matches no step, callbackUrl is not https, the workflow failed validation, or a step can only run on providers whose integration level bars this run (providers names them, level says why): app_only ("In app · own key on API") runs through the API only on your team’s own key, byok_only only on it, and not_allowed / region_restricted not at all. Add your key under Team → Connections, run the workflow from the app, or pick another provider on that step. Messages: Unknown input key(s): foo, callbackUrl must be a valid https:// URL, DiffusionWorker runs on Fal, which is not available through the API without your own key. Add your key under Connections, run this workflow from the app, or pick another provider on that step..
  • 429 Too many requests — Too many requests with this key. By default a key may make 60 requests a minute. Wait for the number of seconds in Retry-After. Message: Rate limit exceeded. Headers: Retry-After.
  • 500 Server error — Something failed on our side. Message: Server error: ….
  • 503 Service unavailable — The key has a monthly credit cap and it could not be checked just now, so the run was refused rather than started unmetered. Retry after Retry-After seconds. Message: Unable to verify monthly credit cap; try again shortly. Headers: Retry-After.
200 OK: response fields
NameDescription
jobIdstring (uuid) · RequiredThe run. Pass it to Get a run.
workflowIdstring (uuid) · RequiredThe workflow that is running.
statusstring · RequiredAlways queued.
estimatedCreditsnumber · RequiredThe estimated cost in credits (1 credit = $0.01). You are charged per step as each finishes.
stepsCountinteger · RequiredNo description.
200 OK: example responseJSON
{
  "jobId": "00000000-0000-0000-0000-000000000000",
  "workflowId": "00000000-0000-0000-0000-000000000000",
  "status": "queued",
  "estimatedCredits": 1,
  "stepsCount": 1
}
202 Accepted: response fields
NameDescription
deferredboolean · RequiredAlways true.
deferredJobIdstring (uuid) · RequiredNo description.
workflowIdstring (uuid) · RequiredNo description.
202 Accepted: example responseJSON
{
  "deferred": true,
  "deferredJobId": "00000000-0000-0000-0000-000000000000",
  "workflowId": "00000000-0000-0000-0000-000000000000"
}
402 Payment required: InsufficientCredits body
NameDescription
errorstring · RequiredAlways Insufficient credits.
requirednumber · RequiredThe credits the run needs held.
402 Payment required: ProjectCapExceeded body
NameDescription
errorstring · RequiredAlways Project hard cap exceeded.
hardCapnumber · RequiredNo description.
spentnumber · RequiredNo description.
estimatenumber · RequiredNo description.
402 Payment required: StepCeilingExceeded body
NameDescription
errorstring · RequiredNo description.
nodeTypestring · RequiredNo description.
estimatedChargenumber · RequiredNo description.
ceilingnumber · RequiredNo description.
402 Payment required: MonthlyCapReached body
NameDescription
errorstring · RequiredAlways Monthly credit cap reached.
monthlyCreditCapnumber · RequiredNo description.
spentThisMonthnumber · RequiredNo description.
resetsstring · RequiredNo description.
422 Unprocessable content: UnknownInputKey body
NameDescription
errorstring · RequiredNo description.
validKeysarray of string · RequiredNo description.
422 Unprocessable content: Error body
NameDescription
errorstring · RequiredWhat went wrong, in plain words.
422 Unprocessable content: RunValidationFailed body
NameDescription
successboolean · RequiredAlways false.
errorsarray of any · RequiredNo description.

POSTRun an agent#

POST https://app.sushibelt.ai/api/v1/agents/{id}/runs

Runs an agent's active workflow. If the agent has no active workflow, its newest workflow runs. The response names the workflow that ran in workflowId. The request body and responses are the same as Run a workflow.

Needs an API key with the runs:create scope. See Scopes.

Parameters#

Run an agent: parameters
NameDescription
idstring (uuid) · in path · RequiredThe agent to run.

Request body#

Every field is optional. An empty or missing body runs the workflow as it was built.

Run an agent: request body fields
NameDescription
inputsobject (key → string) · OptionalValues that replace a step's value for this run, keyed by the step's API input key. A key that matches no step is refused with 422.
modestring · Optionaldraft runs a cheaper pass for trying a workflow out: one variation, fewer sampling steps, short text answers. It is not a discount — a model priced per run costs the same. preview behaves like production. One of: production, draft, preview.Default: "production"
deferboolean · OptionalQueue the run to start later instead of now. The answer is 202 with a deferredJobId. A deferred run that has not started within 24 hours expires. Follow it with Get a deferred run; a callbackUrl passed with it is kept for the run it becomes.Default: false
maxMultipliernumber · OptionalUsed only with defer: the price multiplier the deferred run waits for.Default: 0.7
callbackUrlstring (uri) · OptionalAn https URL to send the Run finished webhook to. Anything other than https is refused with 422.
callbackSecretstring · OptionalA secret of your choosing. When set, each webhook is signed with it in X-Sushibelt-Signature.

Example request#

Set SUSHIBELT_API_KEY to your key first, and replace each YOUR_… value.

Run an agent: request
AGENT_ID="YOUR_AGENT_ID"

curl -X POST "https://app.sushibelt.ai/api/v1/agents/$AGENT_ID/runs" \
  -H "Authorization: Bearer $SUSHIBELT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {
      "subject": "a red fox in snow"
    }
  }'

Responses#

  • 200 OK — The run started.
  • 202 Accepted — With defer: true: the run is queued to start later.
  • 401 Unauthorized — The key is missing, malformed, unknown, revoked or expired. Message: Unauthorized.
  • 402 Payment required — The run would spend more than you allow: the team's balance is below the estimate, the project's hard cap would be passed, one step would pass the team's per-step credit limit, or this key has reached its monthly credit cap. Messages: Insufficient credits, Monthly credit cap reached.
  • 403 Forbidden — The key does not have the runs:create scope, or it is limited to other workflows. Messages: Scope not permitted for this key, Workflow not permitted for this key.
  • 404 Not found — Nothing with this id exists in your team, or the workflow has no canvas. Messages: Agent not found, No workflow found for this agent, Workflow not found, Canvas not found.
  • 409 Conflict — This workflow already has a run in progress.
  • 422 Unprocessable content — An inputs key matches no step, callbackUrl is not https, the workflow failed validation, or a step can only run on providers whose integration level bars this run (providers names them, level says why): app_only ("In app · own key on API") runs through the API only on your team’s own key, byok_only only on it, and not_allowed / region_restricted not at all. Add your key under Team → Connections, run the workflow from the app, or pick another provider on that step. Messages: Unknown input key(s): foo, callbackUrl must be a valid https:// URL, DiffusionWorker runs on Fal, which is not available through the API without your own key. Add your key under Connections, run this workflow from the app, or pick another provider on that step..
  • 429 Too many requests — Too many requests with this key. By default a key may make 60 requests a minute. Wait for the number of seconds in Retry-After. Message: Rate limit exceeded. Headers: Retry-After.
  • 500 Server error — Something failed on our side. Message: Server error: ….
  • 503 Service unavailable — The key has a monthly credit cap and it could not be checked just now, so the run was refused rather than started unmetered. Retry after Retry-After seconds. Message: Unable to verify monthly credit cap; try again shortly. Headers: Retry-After.
200 OK: response fields
NameDescription
jobIdstring (uuid) · RequiredThe run. Pass it to Get a run.
workflowIdstring (uuid) · RequiredThe workflow that is running.
statusstring · RequiredAlways queued.
estimatedCreditsnumber · RequiredThe estimated cost in credits (1 credit = $0.01). You are charged per step as each finishes.
stepsCountinteger · RequiredNo description.
200 OK: example responseJSON
{
  "jobId": "00000000-0000-0000-0000-000000000000",
  "workflowId": "00000000-0000-0000-0000-000000000000",
  "status": "queued",
  "estimatedCredits": 1,
  "stepsCount": 1
}
202 Accepted: response fields
NameDescription
deferredboolean · RequiredAlways true.
deferredJobIdstring (uuid) · RequiredNo description.
workflowIdstring (uuid) · RequiredNo description.
202 Accepted: example responseJSON
{
  "deferred": true,
  "deferredJobId": "00000000-0000-0000-0000-000000000000",
  "workflowId": "00000000-0000-0000-0000-000000000000"
}
402 Payment required: InsufficientCredits body
NameDescription
errorstring · RequiredAlways Insufficient credits.
requirednumber · RequiredThe credits the run needs held.
402 Payment required: ProjectCapExceeded body
NameDescription
errorstring · RequiredAlways Project hard cap exceeded.
hardCapnumber · RequiredNo description.
spentnumber · RequiredNo description.
estimatenumber · RequiredNo description.
402 Payment required: StepCeilingExceeded body
NameDescription
errorstring · RequiredNo description.
nodeTypestring · RequiredNo description.
estimatedChargenumber · RequiredNo description.
ceilingnumber · RequiredNo description.
402 Payment required: MonthlyCapReached body
NameDescription
errorstring · RequiredAlways Monthly credit cap reached.
monthlyCreditCapnumber · RequiredNo description.
spentThisMonthnumber · RequiredNo description.
resetsstring · RequiredNo description.
422 Unprocessable content: UnknownInputKey body
NameDescription
errorstring · RequiredNo description.
validKeysarray of string · RequiredNo description.
422 Unprocessable content: Error body
NameDescription
errorstring · RequiredWhat went wrong, in plain words.
422 Unprocessable content: RunValidationFailed body
NameDescription
successboolean · RequiredAlways false.
errorsarray of any · RequiredNo description.

GETGet a run#

GET https://app.sushibelt.ai/api/v1/runs/{jobId}

Returns a run's status, each step's status, and — once the last steps finish — the output. Poll until status is completed or failed; a few seconds between reads is enough. Reading a run never spends credits, so a key over its monthly cap can still read the runs it started.

Needs an API key with the runs:read scope. See Scopes.

Parameters#

Get a run: parameters
NameDescription
jobIdstring (uuid) · in path · RequiredThe jobId returned when the run started.

Example request#

Set SUSHIBELT_API_KEY to your key first, and replace each YOUR_… value.

Get a run: request
JOB_ID="YOUR_JOB_ID"

curl "https://app.sushibelt.ai/api/v1/runs/$JOB_ID" \
  -H "Authorization: Bearer $SUSHIBELT_API_KEY"

Responses#

  • 200 OK — The run.
  • 401 Unauthorized — The key is missing, malformed, unknown, revoked or expired. Message: Unauthorized.
  • 403 Forbidden — The key does not have the runs:read scope. Message: Scope not permitted for this key.
  • 404 Not found — No run with this id exists in your team. A run in another team reads the same way. Message: Run not found.
  • 429 Too many requests — Too many requests with this key. By default a key may make 60 requests a minute. Wait for the number of seconds in Retry-After. Message: Rate limit exceeded. Headers: Retry-After.
200 OK: response fields
NameDescription
jobIdstring (uuid) · RequiredNo description.
statusstring · RequiredOnce steps exist: processing, then completed or failed (any failed step fails the run). Before that, the run's own state. One of: queued, claimed, processing, completed, failed, cancelled, stopped.
progressinteger · RequiredCompleted steps as a percentage.
stepsarray of RunStep · RequiredNo description.
steps[].node_idstring · RequiredNo description.
steps[].step_typestring · RequiredNo description.
steps[].statusstring · RequiredOne of: queued, processing, completed, failed, skipped, unknown.
steps[].errorstring · OptionalWhy the step failed. Present only on a failed step.
outputarray of RunOutput · RequiredWhat the last steps of the workflow produced — the steps nothing else reads from. Empty until they finish.
output[].node_idstring · RequiredNo description.
output[].step_typestring · RequiredNo description.
output[].resultany · RequiredWhat the step produced — for an image step, the image URLs. The shape depends on the node type. Every file URL is Sushibelt's own copy, marked as AI-generated in its metadata (EU AI Act Art. 50(2)) and signed for two hours; a provider's link is never returned. Each file object carries availability: ready (the url is set), processing (the copy is still being made — poll again) or unavailable (the file could not be stored and marked; url is null).
output[].licenceobject · OptionalThe licence this output ships under: as strict as the strictest model it comes from. Absent when no model step is involved. Not legal advice.
output[].licence.commercial_usestring · RequiredOne of: yes, provider_terms, unchecked, no.
output[].licence.modelsarray of LicenceModel · RequiredNo description.
output[].ai_generatedboolean · RequiredTrue when this output, or any step it comes from, was made by an AI model — the machine-readable marking the EU AI Act (Art. 50) asks for. Images and audio are also marked inside the file.
output[].generated_byarray of object · OptionalThe AI model steps this output comes from. Absent when none.
output[].generated_by[].node_idstring · RequiredNo description.
output[].generated_by[].providerstring · RequiredNo description.
output[].generated_by[].modelstring or null · RequiredNo description.
200 OK: example responseJSON
{
  "jobId": "00000000-0000-0000-0000-000000000000",
  "status": "queued",
  "progress": 1,
  "steps": [
    {
      "node_id": "string",
      "step_type": "string",
      "status": "queued",
      "error": "string"
    }
  ],
  "output": [
    {
      "node_id": "string",
      "step_type": "string",
      "result": null,
      "licence": {
        "commercial_use": "yes",
        "models": [
          {
            "node_id": "string",
            "provider_key": "string",
            "model": "string",
            "licence": "string",
            "licence_url": "string",
            "commercial_use": "yes",
            "terms_url": "string",
            "stamped_at": "2026-09-29T12:00:00Z"
          }
        ]
      },
      "ai_generated": true,
      "generated_by": [
        {
          "node_id": "string",
          "provider": "string",
          "model": "string"
        }
      ]
    }
  ]
}

GETGet a deferred run#

GET https://app.sushibelt.ai/api/v1/deferred-runs/{id}

Returns a run you started with defer: true: whether it is still waiting, has started, expired or was cancelled. Once it has started, jobId is the run it became — read that with Get a run.

Needs an API key with the runs:read scope. See Scopes.

Parameters#

Get a deferred run: parameters
NameDescription
idstring (uuid) · in path · RequiredThe deferredJobId returned when the run was deferred.

Example request#

Set SUSHIBELT_API_KEY to your key first, and replace each YOUR_… value.

Get a deferred run: request
DEFERRED-RUN_ID="YOUR_DEFERRED-RUN_ID"

curl "https://app.sushibelt.ai/api/v1/deferred-runs/$DEFERRED-RUN_ID" \
  -H "Authorization: Bearer $SUSHIBELT_API_KEY"

Responses#

  • 200 OK — The deferred run.
  • 401 Unauthorized — The key is missing, malformed, unknown, revoked or expired. Message: Unauthorized.
  • 403 Forbidden — The key does not have the runs:read scope. Message: Scope not permitted for this key.
  • 404 Not found — No deferred run with this id exists in your team. One in another team reads the same way. Message: Deferred run not found.
  • 429 Too many requests — Too many requests with this key. By default a key may make 60 requests a minute. Wait for the number of seconds in Retry-After. Message: Rate limit exceeded. Headers: Retry-After.
200 OK: response fields
NameDescription
deferredJobIdstring (uuid) · RequiredNo description.
workflowIdstring (uuid) · RequiredNo description.
statusstring · Requiredwaiting until the price multiplier reaches maxMultiplier; dispatched once the run has started (see jobId); expired if it did not start within 24 hours, or could not be paid for or allowed when its turn came; cancelled if its key was revoked or expired, or you cancelled it. One of: waiting, dispatched, expired, cancelled.
maxMultipliernumber · RequiredNo description.
createdAtstring (date-time) · RequiredNo description.
expiresAtstring or null (date-time) · RequiredNo description.
dispatchedAtstring or null (date-time) · RequiredNo description.
jobIdstring or null (uuid) · RequiredThe run it became, once dispatched. Pass it to Get a run.
200 OK: example responseJSON
{
  "deferredJobId": "00000000-0000-0000-0000-000000000000",
  "workflowId": "00000000-0000-0000-0000-000000000000",
  "status": "waiting",
  "maxMultiplier": 1,
  "createdAt": "2026-09-29T12:00:00Z",
  "expiresAt": "2026-09-29T12:00:00Z",
  "dispatchedAt": "2026-09-29T12:00:00Z",
  "jobId": "00000000-0000-0000-0000-000000000000"
}

WebhookRun finished#

We send POST to the callbackUrl you passed when you started the run.

Sent to the callbackUrl you passed when you started a run, once the run completes or fails. The body is the same as Get a run, plus event. Reply with any 2xx status within 8 seconds. Anything else is retried: after 2 minutes, then twice as long each time, at most an hour apart, for 5 attempts in all. Because a delivery can arrive twice, treat jobId and event together as an idempotency key. When you passed callbackSecret, check X-Sushibelt-Signature: compute HMAC-SHA256 of the raw body with your secret, hex-encode it, and compare it to the value after sha256= in constant time.

Headers#

Run finished: headers
NameDescription
X-Sushibelt-Eventstring · in header · RequiredWhat happened: run.completed, run.failed, run.stopped (someone stopped it) or run.cancelled (someone cancelled it).
X-Sushibelt-Signaturestring · in header · OptionalSent only when you passed callbackSecret: sha256= followed by the hex HMAC-SHA256 of the raw body.

Body#

The same fields as Get a run, plus event.

Run finished: body fields
NameDescription
eventstring · RequiredOne of: run.completed, run.failed, run.stopped, run.cancelled.
jobIdstring (uuid) · RequiredNo description.
statusstring · RequiredNo description.
progressinteger · OptionalNo description.
stepsarray of RunStep · OptionalNo description.
steps[].node_idstring · RequiredNo description.
steps[].step_typestring · RequiredNo description.
steps[].statusstring · RequiredOne of: queued, processing, completed, failed, skipped, unknown.
steps[].errorstring · OptionalWhy the step failed. Present only on a failed step.
outputarray of RunOutput · OptionalNo description.
output[].node_idstring · RequiredNo description.
output[].step_typestring · RequiredNo description.
output[].resultany · RequiredWhat the step produced — for an image step, the image URLs. The shape depends on the node type. Every file URL is Sushibelt's own copy, marked as AI-generated in its metadata (EU AI Act Art. 50(2)) and signed for two hours; a provider's link is never returned. Each file object carries availability: ready (the url is set), processing (the copy is still being made — poll again) or unavailable (the file could not be stored and marked; url is null).
output[].licenceobject · OptionalThe licence this output ships under: as strict as the strictest model it comes from. Absent when no model step is involved. Not legal advice.
output[].licence.commercial_usestring · RequiredOne of: yes, provider_terms, unchecked, no.
output[].licence.modelsarray of LicenceModel · RequiredNo description.
output[].ai_generatedboolean · RequiredTrue when this output, or any step it comes from, was made by an AI model — the machine-readable marking the EU AI Act (Art. 50) asks for. Images and audio are also marked inside the file.
output[].generated_byarray of object · OptionalThe AI model steps this output comes from. Absent when none.
output[].generated_by[].node_idstring · RequiredNo description.
output[].generated_by[].providerstring · RequiredNo description.
output[].generated_by[].modelstring or null · RequiredNo description.

Example delivery#

Example delivery bodyJSON
{
  "event": "run.completed",
  "jobId": "00000000-0000-0000-0000-000000000000",
  "status": "string",
  "progress": 1,
  "steps": [
    {
      "node_id": "string",
      "step_type": "string",
      "status": "queued",
      "error": "string"
    }
  ],
  "output": [
    {
      "node_id": "string",
      "step_type": "string",
      "result": null,
      "licence": {
        "commercial_use": "yes",
        "models": [
          {
            "node_id": "string",
            "provider_key": "string",
            "model": "string",
            "licence": "string",
            "licence_url": "string",
            "commercial_use": "yes",
            "terms_url": "string",
            "stamped_at": "2026-09-29T12:00:00Z"
          }
        ]
      },
      "ai_generated": true,
      "generated_by": [
        {
          "node_id": "string",
          "provider": "string",
          "model": "string"
        }
      ]
    }
  ]
}

Your response#

  • 2XX Success — You received the delivery. Nothing is retried.

Next steps