{"openapi":"3.1.0","info":{"title":"Sushibelt API","version":"1.0.0","summary":"Create and run Sushibelt workflows from your own code.","description":"The Sushibelt API lets you list and create workflows, start runs, and read their status and output. A run started through the API is the same as a run started from the canvas: it is estimated first, credits are held, each step is charged when it finishes, and outputs land in your team's library.\n\nRequests and responses are JSON. Every request except the one that fetches this document needs a team API key."},"servers":[{"url":"https://app.sushibelt.ai/api/v1","description":"Production"}],"tags":[{"name":"Workflows","description":"Find the workflows a key can run, and create new ones from a list of steps."},{"name":"Runs","description":"Start a run of a workflow or an agent, then read its status and output — by polling, or by a webhook when it finishes."},{"name":"Specification","description":"This API description itself, as OpenAPI 3.1, for code generators and agents."}],"paths":{"/workflows":{"get":{"operationId":"listWorkflows","summary":"List workflows","description":"Lists the workflows this key can run, newest change first. Only your team's workflows are listed. If the key is limited to certain workflows, only those are listed. There is no pagination: every workflow is returned.","tags":["Workflows"],"security":[{"teamApiKey":["workflows:read"]}],"responses":{"200":{"description":"The workflows this key can run. An empty list means the team has none, or the key may run none.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key does not have the `workflows:read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Scope not permitted for this key","value":{"error":"Scope not permitted for this key"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createWorkflow","summary":"Create a workflow","description":"Creates a workflow from a list of steps. Sushibelt turns the steps into a canvas graph for you. The workflow belongs to an agent: pass `agentId` to add it to an existing agent, or `projectId` to create a new agent for it. Keys limited to certain workflows cannot create workflows.","tags":["Workflows"],"security":[{"teamApiKey":["workflows:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkflowRequest"},"examples":{"portrait":{"summary":"A prompt, an image model and a preview","value":{"name":"Fox portrait","projectId":"00000000-0000-0000-0000-000000000000","steps":[{"key":"subject","type":"TextEntry","config":{"text":"a red fox","apiInputKey":"subject"}},{"key":"image","type":"DiffusionWorker","inputs":["subject"]},{"key":"preview","type":"ImageGallery","inputs":["image"]}]}}}}}},"responses":{"201":{"description":"The workflow was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkflowResponse"}}}},"400":{"description":"The request is missing something: a name, at least one step, or exactly one of `agentId` and `projectId`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"name is required","value":{"error":"name is required"}},"e2":{"summary":"steps is required","value":{"error":"steps is required"}},"e3":{"summary":"Provide exactly one of agentId or projectId","value":{"error":"Provide exactly one of agentId or projectId"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key does not have the `workflows:write` scope, or it is limited to certain workflows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Scope not permitted for this key","value":{"error":"Scope not permitted for this key"}},"e2":{"summary":"This key is not permitted to create workflows","value":{"error":"This key is not permitted to create workflows"}}}}}},"404":{"description":"The agent or project does not exist in your team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Agent or project not found","value":{"error":"Agent or project not found"}}}}}},"422":{"description":"The steps do not make a valid workflow: an unknown node type, a repeated step key, an input that names no step, or a graph that fails validation (the reasons are listed in `errors`).","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/WorkflowValidationFailed"}]},"examples":{"unknownType":{"summary":"Unknown node type","value":{"error":"Unknown node type: \"Foo\""}},"invalid":{"summary":"Validation failed","value":{"error":"Workflow validation failed","errors":["…"]}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/workflows/{id}/runs":{"post":{"operationId":"runWorkflow","summary":"Run a workflow","description":"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.","tags":["Runs"],"security":[{"teamApiKey":["runs:create"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The workflow to run.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunRequest"},"examples":{"inputs":{"summary":"Replace an input","value":{"inputs":{"subject":"a red fox in snow"}}},"webhook":{"summary":"Be told when it finishes","value":{"inputs":{"subject":"a red fox in snow"},"callbackUrl":"https://example.com/hooks/sushibelt","callbackSecret":"YOUR_WEBHOOK_SECRET"}}}}}},"responses":{"200":{"description":"The run started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunStarted"}}}},"202":{"description":"With `defer: true`: the run is queued to start later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDeferred"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"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.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/InsufficientCredits"},{"$ref":"#/components/schemas/ProjectCapExceeded"},{"$ref":"#/components/schemas/StepCeilingExceeded"},{"$ref":"#/components/schemas/MonthlyCapReached"}]},"examples":{"credits":{"summary":"Not enough credits","value":{"error":"Insufficient credits","required":12}},"cap":{"summary":"Monthly cap reached","value":{"error":"Monthly credit cap reached","monthlyCreditCap":500,"spentThisMonth":500,"resets":"on the 1st of next month"}}}}}},"403":{"description":"The key does not have the `runs:create` scope, or it is limited to other workflows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Scope not permitted for this key","value":{"error":"Scope not permitted for this key"}},"e2":{"summary":"Workflow not permitted for this key","value":{"error":"Workflow not permitted for this key"}}}}}},"404":{"description":"Nothing with this id exists in your team, or the workflow has no canvas.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Workflow not found","value":{"error":"Workflow not found"}},"e2":{"summary":"Canvas not found","value":{"error":"Canvas not found"}}}}}},"409":{"description":"This workflow already has a run in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlreadyRunning"}}}},"422":{"description":"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.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/UnknownInputKey"},{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RunValidationFailed"}]},"examples":{"unknownInput":{"summary":"Unknown input key","value":{"error":"Unknown input key(s): foo","validKeys":["subject"]}},"callback":{"summary":"Callback not https","value":{"error":"callbackUrl must be a valid https:// URL"}},"notOnApi":{"summary":"Provider not available through the API without your own key","value":{"error":"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.","providers":["fal_ai"],"level":"app_only"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"$ref":"#/components/responses/CapUnverifiable"}}}},"/agents/{id}/runs":{"post":{"operationId":"runAgent","summary":"Run an agent","description":"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.","tags":["Runs"],"security":[{"teamApiKey":["runs:create"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The agent to run.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunRequest"},"examples":{"inputs":{"summary":"Replace an input","value":{"inputs":{"subject":"a red fox in snow"}}},"webhook":{"summary":"Be told when it finishes","value":{"inputs":{"subject":"a red fox in snow"},"callbackUrl":"https://example.com/hooks/sushibelt","callbackSecret":"YOUR_WEBHOOK_SECRET"}}}}}},"responses":{"200":{"description":"The run started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunStarted"}}}},"202":{"description":"With `defer: true`: the run is queued to start later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDeferred"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"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.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/InsufficientCredits"},{"$ref":"#/components/schemas/ProjectCapExceeded"},{"$ref":"#/components/schemas/StepCeilingExceeded"},{"$ref":"#/components/schemas/MonthlyCapReached"}]},"examples":{"credits":{"summary":"Not enough credits","value":{"error":"Insufficient credits","required":12}},"cap":{"summary":"Monthly cap reached","value":{"error":"Monthly credit cap reached","monthlyCreditCap":500,"spentThisMonth":500,"resets":"on the 1st of next month"}}}}}},"403":{"description":"The key does not have the `runs:create` scope, or it is limited to other workflows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Scope not permitted for this key","value":{"error":"Scope not permitted for this key"}},"e2":{"summary":"Workflow not permitted for this key","value":{"error":"Workflow not permitted for this key"}}}}}},"404":{"description":"Nothing with this id exists in your team, or the workflow has no canvas.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Agent not found","value":{"error":"Agent not found"}},"e2":{"summary":"No workflow found for this agent","value":{"error":"No workflow found for this agent"}},"e3":{"summary":"Workflow not found","value":{"error":"Workflow not found"}},"e4":{"summary":"Canvas not found","value":{"error":"Canvas not found"}}}}}},"409":{"description":"This workflow already has a run in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlreadyRunning"}}}},"422":{"description":"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.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/UnknownInputKey"},{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RunValidationFailed"}]},"examples":{"unknownInput":{"summary":"Unknown input key","value":{"error":"Unknown input key(s): foo","validKeys":["subject"]}},"callback":{"summary":"Callback not https","value":{"error":"callbackUrl must be a valid https:// URL"}},"notOnApi":{"summary":"Provider not available through the API without your own key","value":{"error":"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.","providers":["fal_ai"],"level":"app_only"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"$ref":"#/components/responses/CapUnverifiable"}}}},"/runs/{jobId}":{"get":{"operationId":"getRun","summary":"Get a run","description":"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.","tags":["Runs"],"security":[{"teamApiKey":["runs:read"]}],"parameters":[{"name":"jobId","in":"path","required":true,"description":"The `jobId` returned when the run started.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key does not have the `runs:read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Scope not permitted for this key","value":{"error":"Scope not permitted for this key"}}}}}},"404":{"description":"No run with this id exists in your team. A run in another team reads the same way.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Run not found","value":{"error":"Run not found"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/deferred-runs/{id}":{"get":{"operationId":"getDeferredRun","summary":"Get a deferred run","description":"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.","tags":["Runs"],"security":[{"teamApiKey":["runs:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The `deferredJobId` returned when the run was deferred.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The deferred run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeferredRun"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key does not have the `runs:read` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Scope not permitted for this key","value":{"error":"Scope not permitted for this key"}}}}}},"404":{"description":"No deferred run with this id exists in your team. One in another team reads the same way.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"e1":{"summary":"Deferred run not found","value":{"error":"Deferred run not found"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"Get this API description","description":"Returns this document: the whole API as OpenAPI 3.1. No key is needed.","tags":["Specification"],"security":[],"responses":{"200":{"description":"The OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object","description":"An OpenAPI 3.1 document."}}}}}}}},"webhooks":{"runFinished":{"post":{"operationId":"runFinished","summary":"Run finished","description":"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.","tags":["Runs"],"security":[],"parameters":[{"name":"X-Sushibelt-Event","in":"header","required":true,"description":"What happened: `run.completed`, `run.failed`, `run.stopped` (someone stopped it) or `run.cancelled` (someone cancelled it).","schema":{"type":"string","enum":["run.completed","run.failed","run.stopped","run.cancelled"]}},{"name":"X-Sushibelt-Signature","in":"header","required":false,"description":"Sent only when you passed `callbackSecret`: `sha256=` followed by the hex HMAC-SHA256 of the raw body.","schema":{"type":"string","examples":["sha256=5d41402abc4b2a76b9719d911017c592…"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunWebhookPayload"}}}},"responses":{"2XX":{"description":"You received the delivery. Nothing is retried."}}}}},"components":{"securitySchemes":{"teamApiKey":{"type":"http","scheme":"bearer","bearerFormat":"sk_live_…","description":"A team API key, sent as `Authorization: Bearer sk_live_…`. Create one in the app under Team settings → API keys; the full key is shown once, when you create it. A key belongs to one team and reaches only that team's workflows and runs. A key can be limited to certain workflows, to certain scopes, and to a monthly credit cap; with no scopes set, it has all of them. A missing, unknown, revoked or expired key is refused with 401.","x-scopes":{"workflows:read":"List the workflows a key can run.","workflows:write":"Create a workflow from a step list.","runs:create":"Start a run of a workflow or an agent. The only scope that spends credits, and the only one a monthly credit cap can refuse.","runs:read":"Read a run's status, steps and output."}}},"schemas":{"Error":{"type":"object","description":"Most errors: a message, and sometimes more fields that explain it.","properties":{"error":{"type":"string","description":"What went wrong, in plain words."}},"required":["error"]},"WorkflowSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Pass this to Run a workflow."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":["string","null"],"enum":["draft","active","archived",null]},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","name","description","status","updated_at"]},"WorkflowList":{"type":"object","properties":{"workflows":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowSummary"}}},"required":["workflows"]},"WorkflowStepInput":{"type":"object","description":"A connection from another step, naming the handles. Leave the handles out to use each node's first one.","properties":{"from":{"type":"string","description":"The `key` of the step to connect from."},"sourceHandle":{"type":"string","description":"The output handle on that step."},"targetHandle":{"type":"string","description":"The input handle on this step."}},"required":["from"]},"WorkflowStep":{"type":"object","description":"One node of the workflow.","properties":{"key":{"type":"string","description":"A name for this step, unique within the request. Other steps refer to it in `inputs`.","examples":["image"]},"type":{"type":"string","description":"The node type.","enum":["InputStage","ProcessingStage","ExportStage","Prompt","Knowledgebase","Service","Github","CloudStorage","ImageGen","AudioGen","3DTransform","TextEntry","FileUploader","JsonLoader","ImageLoader","WebhookListener","S3BucketWatcher","ShaderSnippet","RigImporter","TextureManifest","PromptBuilder","JsonExtractor","VariableMapper","MathOperator","ConditionalSwitch","BatchSplitter","ImageCropper","MeshDecimator","CustomApiCall","PythonSandbox","IterationGate","LlmWorker","DiffusionWorker","ControlNetFilter","VisionInterrogator","DepthEstimator","PbrSynthesizer","TwoDToThreeD","AutoRigger","Upscaler","BackgroundRemover","Embedder","TextConsole","ImageGallery","AudioPlayer","JsonInspector","ApiPostAction","S3Uploader","DiscordSlack","GithubDelivery","GitlabDelivery","PlasticDelivery","UnityDelivery","ThreeDStudioViewer","ShaderPlayback"]},"label":{"type":"string","description":"The name shown on the canvas. Defaults to the node type's name."},"config":{"type":"object","additionalProperties":true,"description":"The node's settings; the fields depend on the node type. Set `apiInputKey` on a TextEntry or PromptBuilder step to let a run replace its value."},"inputs":{"type":"array","description":"The steps this step reads from. A step key alone connects first output to first input.","items":{"oneOf":[{"type":"string","description":"A step key."},{"$ref":"#/components/schemas/WorkflowStepInput"}]}}},"required":["key","type"]},"CreateWorkflowRequest":{"type":"object","description":"Give exactly one of `agentId` and `projectId`.","properties":{"name":{"type":"string","description":"The workflow's name."},"agentId":{"type":"string","format":"uuid","description":"Add the workflow to this agent."},"projectId":{"type":"string","format":"uuid","description":"Create a new agent in this project and add the workflow to it."},"agentName":{"type":"string","description":"The new agent's name, when you pass `projectId`. Defaults to `name`."},"steps":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/WorkflowStep"}}},"required":["name","steps"]},"CreateWorkflowResponse":{"type":"object","properties":{"workflowId":{"type":"string","format":"uuid"},"agentId":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"stepsCount":{"type":"integer"}},"required":["workflowId","agentId","projectId","stepsCount"]},"WorkflowValidationFailed":{"type":"object","description":"The workflow failed validation. `errors` lists each problem.","properties":{"error":{"type":"string","const":"Workflow validation failed"},"errors":{"type":"array","items":{}}},"required":["error","errors"]},"RunRequest":{"type":"object","description":"Every field is optional. An empty or missing body runs the workflow as it was built.","properties":{"inputs":{"type":"object","additionalProperties":{"type":"string"},"description":"Values 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.","examples":[{"subject":"a red fox in snow"}]},"mode":{"type":"string","enum":["production","draft","preview"],"default":"production","description":"`draft` 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`."},"defer":{"type":"boolean","default":false,"description":"Queue 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."},"maxMultiplier":{"type":"number","default":0.7,"description":"Used only with `defer`: the price multiplier the deferred run waits for."},"callbackUrl":{"type":"string","format":"uri","description":"An https URL to send the Run finished webhook to. Anything other than https is refused with 422."},"callbackSecret":{"type":"string","description":"A secret of your choosing. When set, each webhook is signed with it in `X-Sushibelt-Signature`."}}},"RunStarted":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","description":"The run. Pass it to Get a run."},"workflowId":{"type":"string","format":"uuid","description":"The workflow that is running."},"status":{"type":"string","const":"queued"},"estimatedCredits":{"type":"number","description":"The estimated cost in credits (1 credit = $0.01). You are charged per step as each finishes."},"stepsCount":{"type":"integer"}},"required":["jobId","workflowId","status","estimatedCredits","stepsCount"]},"RunDeferred":{"type":"object","description":"Accepted to start later. Not a promise that it will: if the key is revoked or expires meanwhile, the run is cancelled; if the key is over its monthly cap, it keeps waiting.","properties":{"deferred":{"type":"boolean","const":true},"deferredJobId":{"type":"string","format":"uuid"},"workflowId":{"type":"string","format":"uuid"}},"required":["deferred","deferredJobId","workflowId"]},"DeferredRun":{"type":"object","properties":{"deferredJobId":{"type":"string","format":"uuid"},"workflowId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["waiting","dispatched","expired","cancelled"],"description":"`waiting` 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."},"maxMultiplier":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"dispatchedAt":{"type":["string","null"],"format":"date-time"},"jobId":{"type":["string","null"],"format":"uuid","description":"The run it became, once `dispatched`. Pass it to Get a run."}},"required":["deferredJobId","workflowId","status","maxMultiplier","createdAt","expiresAt","dispatchedAt","jobId"]},"LicenceModel":{"type":"object","description":"One model step an output comes from, and its licence as it stood when that step ran.","properties":{"node_id":{"type":"string"},"provider_key":{"type":"string","description":"The provider that ran the model."},"model":{"type":"string"},"licence":{"type":["string","null"]},"licence_url":{"type":["string","null"]},"commercial_use":{"type":["string","null"],"enum":["yes","provider_terms","no",null],"description":"`yes`, `provider_terms` (the provider's terms grant the output), `no`, or null when it had not been checked."},"terms_url":{"type":["string","null"],"description":"The provider's own terms."},"stamped_at":{"type":"string","format":"date-time"}},"required":["node_id","provider_key","model","licence","licence_url","commercial_use","terms_url","stamped_at"]},"RunStep":{"type":"object","properties":{"node_id":{"type":"string"},"step_type":{"type":"string"},"status":{"type":"string","enum":["queued","processing","completed","failed","skipped","unknown"]},"error":{"type":"string","description":"Why the step failed. Present only on a failed step."}},"required":["node_id","step_type","status"]},"RunOutput":{"type":"object","properties":{"node_id":{"type":"string"},"step_type":{"type":"string"},"result":{"description":"What 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)."},"licence":{"type":"object","description":"The licence this output ships under: as strict as the strictest model it comes from. Absent when no model step is involved. Not legal advice.","properties":{"commercial_use":{"type":"string","enum":["yes","provider_terms","unchecked","no"]},"models":{"type":"array","items":{"$ref":"#/components/schemas/LicenceModel"}}},"required":["commercial_use","models"]},"ai_generated":{"type":"boolean","description":"True 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."},"generated_by":{"type":"array","description":"The AI model steps this output comes from. Absent when none.","items":{"type":"object","properties":{"node_id":{"type":"string"},"provider":{"type":"string"},"model":{"type":["string","null"]}},"required":["node_id","provider","model"]}}},"required":["node_id","step_type","result","ai_generated"]},"RunResult":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","claimed","processing","completed","failed","cancelled","stopped"],"description":"Once steps exist: `processing`, then `completed` or `failed` (any failed step fails the run). Before that, the run's own state."},"progress":{"type":"integer","minimum":0,"maximum":100,"description":"Completed steps as a percentage."},"steps":{"type":"array","items":{"$ref":"#/components/schemas/RunStep"}},"output":{"type":"array","items":{"$ref":"#/components/schemas/RunOutput"},"description":"What the last steps of the workflow produced — the steps nothing else reads from. Empty until they finish."}},"required":["jobId","status","progress","steps","output"]},"RunWebhookPayload":{"type":"object","description":"The same fields as Get a run, plus `event`.","properties":{"event":{"type":"string","enum":["run.completed","run.failed","run.stopped","run.cancelled"]},"jobId":{"type":"string","format":"uuid"},"status":{"type":"string"},"progress":{"type":"integer"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/RunStep"}},"output":{"type":"array","items":{"$ref":"#/components/schemas/RunOutput"}}},"required":["event","jobId","status"]},"InsufficientCredits":{"type":"object","properties":{"error":{"type":"string","const":"Insufficient credits"},"required":{"type":"number","description":"The credits the run needs held."}},"required":["error","required"]},"ProjectCapExceeded":{"type":"object","properties":{"error":{"type":"string","const":"Project hard cap exceeded"},"hardCap":{"type":"number"},"spent":{"type":"number"},"estimate":{"type":"number"}},"required":["error","hardCap","spent","estimate"]},"StepCeilingExceeded":{"type":"object","description":"One step would cost more than your team's per-step credit limit, so the run did not start.","properties":{"error":{"type":"string"},"nodeType":{"type":"string"},"estimatedCharge":{"type":"number"},"ceiling":{"type":"number"}},"required":["error","nodeType","estimatedCharge","ceiling"]},"MonthlyCapReached":{"type":"object","properties":{"error":{"type":"string","const":"Monthly credit cap reached"},"monthlyCreditCap":{"type":"number"},"spentThisMonth":{"type":"number"},"resets":{"type":"string","examples":["on the 1st of next month"]}},"required":["error","monthlyCreditCap","spentThisMonth","resets"]},"UnknownInputKey":{"type":"object","properties":{"error":{"type":"string","examples":["Unknown input key(s): foo"]},"validKeys":{"type":"array","items":{"type":"string"}}},"required":["error","validKeys"]},"RunValidationFailed":{"type":"object","description":"The workflow failed validation before it ran. Note: this body has no `error` field.","properties":{"success":{"type":"boolean","const":false},"errors":{"type":"array","items":{}}},"required":["success","errors"]},"AlreadyRunning":{"type":"object","properties":{"error":{"type":"string","const":"This workflow already has a run in progress"},"activeJobId":{"type":"string","description":"The run in progress. Read it, or wait for it to finish."}},"required":["error","activeJobId"]}},"responses":{"Unauthorized":{"description":"The key is missing, malformed, unknown, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthorized":{"value":{"error":"Unauthorized"}}}}}},"RateLimited":{"description":"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`.","headers":{"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer","examples":[60]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"limited":{"value":{"error":"Rate limit exceeded"}}}}}},"CapUnverifiable":{"description":"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.","headers":{"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer","examples":[30]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unverifiable":{"value":{"error":"Unable to verify monthly credit cap; try again shortly"}}}}}},"ServerError":{"description":"Something failed on our side.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server":{"value":{"error":"Server error: …"}}}}}}}}}