openapi: 3.1.0 info: title: 'Integrations Service API' description: 'API for the revenexx Integration Studio — connects Revenue Cloud to external systems (ERP, PIM, CRM) via Temporal workflows.' version: 1.0.0 servers: - url: 'https://integrations.revenexx.com' tags: - name: 'Audit Log' description: "\nRead access to the append-only audit trail (credential / secret / workflow /\ntrigger create-edit-delete, plus workflow run start/finished/failed). Two\nplanes: the public feed is auto-scoped to the caller's org + tenant; the\nadmin feed (registered under `v1/admin`) filters all three identity fields\noptionally. Results are newest-first and cursor-paginated." - name: Auth description: "\nEndpoints for verifying authentication and tenant context." - name: 'Credential Types' description: "\nRead-only catalogue of the credential types registered via node packages.\nDrives the credential form (fields + validation) and the `credentials-ref`\ntype picker in the editor. Globally registered, not tenant-scoped." - name: Credentials description: "\nTenant-scoped credential instances of a registered {@see CredentialType}.\nThe encrypted `config` / `durable_creds` blobs are never returned. Runtime\nresolution happens in the broker, not here — this controller only stores and\nproxies the on-demand connection test." - name: 'Node Packages' description: '' - name: Nodes description: "\nGlobally registered node definitions, addressed publicly by\n`/`. Read endpoints additionally accept the alias\n`latest` in place of an explicit version. The `DELETE` endpoint refuses\nthe alias and requires an exact version, so the caller always knows which\nrow they're touching.\n\nNodes are created and updated exclusively through the node-package upload\n(`POST /v1/admin/orgs/{org_id}/node-packages`), which extracts the manifests\nfrom the published tarball — there is no standalone create/update endpoint,\nsince a node always ships with its runtime code.\n\nAuthentication is required for every endpoint, but the resource itself\nis *not* tenant-scoped — per-tenant entitlement (which tenant may use\nwhich node) is the responsibility of a future app-registration flow." - name: 'Run Dead Letters' description: "\nA trigger firing that could not start a run — and left no failed run row to\nretry — is captured here so it can be replayed once the cause is resolved\n(typically the workflow bundle finishing its build) or explicitly discarded." - name: Runs description: "\nEvery run of every workflow the tenant owns, in one list the platform orders\nand pages itself. The studio's Runs page reads this; a workflow's own Runs tab\nreads the per-workflow subresource\n(`GET /workflows/{workflowId}/runs`), whose contract is unchanged.\n\nIts own controller rather than more methods on\n{@see WorkflowRunController}: that class orchestrates starting, cancelling,\nresuming and inspecting a run and carries the services to do it. This is a\nread, and it needs none of them." - name: Schedules description: "\nEvery scheduled trigger the tenant owns, on one list, each saying when it\nfires next and when it last really ran.\n\nThe per-workflow trigger list cannot answer this surface's questions: how many\nschedules are live across the tenant, and which timezones its schedules are\nactually in, are counts over the tenant rather than over one workflow — so a\nclient assembling them from a fan-out could only ever report what it had\nmanaged to load." - name: Schemas description: "\nPublic, read-only access to the JSON-Schema definitions used to validate\nversioned payloads (currently the workflow `blob` and the node manifest).\nSchemas are global — they describe the shape of payloads regardless of\ntenant — so these endpoints are intentionally unauthenticated." - name: 'State Store' description: "\nThe operator's window into the tenant state store (PO-374): which namespaces\nexist, what they hold, and the ability to correct a single entry when a sync\nwent wrong.\n\nThis exists because the support question is never abstract — it is \"why was\narticle X created twice?\", and the answer is one lookup away if somebody can\nsee the correlation and the run that wrote it. Without this view the store\nwould be a black box that only the workflow author can reason about.\n\nDeliberately read-and-correct, not read-and-write: there is no bulk import.\nThat is the door through which \"let's just load our master data in here\"\nwalks in, and the store is not a database." - name: Templates description: "\nCatalogue of the workflow templates registered via node packages, plus the\n\"install\" actions a UI uses to turn one into a real workflow. Listing/showing\nis global; instantiation is scoped to the current tenant." - name: 'Tenant Secrets' description: "\nPer-tenant encrypted key/value store. Values are AES-256-GCM encrypted at\nrest using a dedicated SECRETS_ENCRYPTION_KEY (separate from APP_KEY) and\nare never returned by the API." - name: Triggers description: "\nManage triggers for a workflow. Triggers are the entry points that can start\na workflow execution (manual, schedule, webhook, or event). They live as DB entities\nrather than inside the workflow blob, and are referenced in blob edges via\ntheir stable UUID handle." - name: Webhooks description: "\nInbound endpoint that fires a workflow run for a webhook trigger. The\ntrigger is addressed by its UUID handle in the URL; authentication is the\nstandard JWT + `X-Tenant-Id` used by the rest of the API, so the trigger is\nresolved within the caller's tenant. The HTTP request (body, query,\nheaders, method) is delivered to the workflow as the trigger payload,\nreachable via `${{ trigger.payload.* }}`." - name: 'Workflow Credentials' description: "\nRead-only introspection of which typed credentials a workflow's nodes\nreference through their `credentials-ref` config fields. Useful for pre-run\nchecks, access auditing, and impact analysis when a credential instance is\nrotated or deleted. The credential *values* are never touched here — only\nthe instance UUIDs and their credential type slugs are reported." - name: 'Workflow Revisions' description: "\nRead the archived revision history of a workflow and restore an old\nrevision. Every blob change to a workflow snapshots the previous revision\ninto `workflow_histories`; these endpoints expose that history and let a\nclient re-apply an old revision as a brand-new one.\n\nAll endpoints are scoped to the tenant resolved by the `resolve.tenant`\nmiddleware. Cross-tenant access returns 404 so the API never leaks the\nexistence of resources owned by other tenants." - name: 'Workflow Runs' description: "\nTrigger and inspect executions of a workflow. Each run is handed off to\nthe workflow's dedicated Temporal worker, which orchestrates node\nexecution and streams status back." - name: Workflows description: "\nPer-tenant workflow definitions. Every endpoint is scoped to the tenant\nresolved by the `resolve.tenant` middleware (from the JWT or\n`X-Tenant-Id` header). Cross-tenant access deliberately returns 404 so\nthat the API does not leak the existence of resources owned by other\ntenants." components: securitySchemes: default: type: http scheme: bearer description: 'Authenticate via a Zitadel-issued OIDC JWT. Tenant context is taken from the `X-Tenant-Id` request header; the `tenant_id` JWT claim is accepted as a legacy fallback for development tokens.' responses: TooManyRequests: description: 'Too many requests — the rate limit has been exceeded. Wait for the period in the Retry-After header before retrying. The authenticated API allows 300 requests/minute (30/second burst) per user.' headers: Retry-After: description: 'Seconds to wait before retrying.' schema: type: integer content: application/json: schema: type: object properties: message: type: string example: message: 'Too Many Attempts.' security: - default: [] paths: /api/v1/audit-logs: get: summary: 'List audit entries (public)' operationId: listAuditEntriespublic description: "Returns the audit entries for the caller's org + tenant, newest first.\nOptionally narrowed by `sub` (actor), `resource_type` and `action`." parameters: - in: query name: sub description: 'optional Filter by acting user subject.' required: false schema: type: string description: 'optional Filter by acting user subject.' examples: - '289347298347' - in: query name: resource_type description: 'optional Filter by resource type (credential, secret, workflow, trigger, workflow_run).' required: false schema: type: string description: 'optional Filter by resource type (credential, secret, workflow, trigger, workflow_run).' examples: - workflow - in: query name: action description: 'optional Filter by action (create, update, delete, run.started, run.finished, run.failed).' required: false schema: type: string description: 'optional Filter by action (create, update, delete, run.started, run.finished, run.failed).' examples: - create - in: query name: limit description: 'optional Page size (1–100, default 50).' required: false schema: type: integer description: 'optional Page size (1–100, default 50).' examples: - 50 - in: query name: cursor description: 'optional Opaque pagination cursor from a previous response.' required: false schema: type: string description: 'optional Opaque pagination cursor from a previous response.' examples: - eyJpZCI6NDJ9 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 42 org_id: type: string examples: - acme-org tenant_id: type: string examples: - acme-production actor_sub: type: - string - 'null' description: 'The acting user subject (`sub`); null for system-initiated entries (`via_system` = true).' examples: - '289347298347' via_system: type: boolean examples: - false channel: type: string examples: - api action: type: string examples: - create resource_type: type: string examples: - workflow resource_id: type: string examples: - '1' metadata: type: - object - 'null' properties: name: type: string examples: - 'Sync orders' description: 'Action-specific detail recorded with the entry; null when none was captured.' created_at: type: string examples: - '2026-06-18T10:00:00Z' examples: - - id: 42 org_id: acme-org tenant_id: acme-production actor_sub: '289347298347' via_system: false channel: api action: create resource_type: workflow resource_id: '1' metadata: name: 'Sync orders' created_at: '2026-06-18T10:00:00Z' meta: type: object properties: per_page: type: integer examples: - 50 next_cursor: type: - string - 'null' examples: - null prev_cursor: type: - string - 'null' examples: - null examples: - data: - id: 42 org_id: acme-org tenant_id: acme-production actor_sub: '289347298347' via_system: false channel: api action: create resource_type: workflow resource_id: '1' metadata: name: 'Sync orders' created_at: '2026-06-18T10:00:00Z' meta: per_page: 50 next_cursor: null prev_cursor: null 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Audit Log' /api/v1/me: get: summary: 'Get authenticated user info' operationId: getAuthenticatedUserInfo description: "Returns full user profile, resolved tenant context, and raw token claims.\nUseful for debugging authentication and understanding which data Zitadel provides." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: user: type: object properties: id: type: integer examples: - 1 zitadel_id: type: string examples: - '289347298347' email: type: string examples: - user@acme.com name: type: string examples: - 'Jane Doe' context: type: object properties: tenant_id: type: string examples: - acme-production active_plane: type: string examples: - public roles: type: array items: type: string examples: - - admin - user claims: type: object properties: iss: type: string examples: - 'https://id.revenexx.com' sub: type: string examples: - '289347298347' aud: type: array items: type: string examples: - - project-id examples: - user: id: 1 zitadel_id: '289347298347' email: user@acme.com name: 'Jane Doe' context: tenant_id: acme-production active_plane: public roles: - admin - user claims: iss: 'https://id.revenexx.com' sub: '289347298347' aud: - project-id 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 429: $ref: '#/components/responses/TooManyRequests' tags: - Auth /api/v1/credential-types: get: summary: 'List credential types' operationId: listCredentialTypes description: 'Returns the latest registered version of each credential type.' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: slug: type: string examples: - 'revenexx:smtp' version: type: string examples: - 1.0.0 name: type: string examples: - SMTP description: type: string examples: - 'Send mail over SMTP' icon: type: - string - 'null' examples: - null images: type: array items: type: object properties: src: type: string examples: - 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: type: object properties: en: type: string examples: - 'SMTP logo' title: type: object properties: en: type: string examples: - SMTP category: type: string examples: - logo examples: - - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'SMTP logo' title: en: SMTP category: logo auth_kind: type: string examples: - secret fields: type: array examples: - [] examples: - - slug: 'revenexx:smtp' version: 1.0.0 name: SMTP description: 'Send mail over SMTP' icon: null images: - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'SMTP logo' title: en: SMTP category: logo auth_kind: secret fields: [] examples: - data: - slug: 'revenexx:smtp' version: 1.0.0 name: SMTP description: 'Send mail over SMTP' icon: null images: - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'SMTP logo' title: en: SMTP category: logo auth_kind: secret fields: [] 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Credential Types' '/api/v1/credential-types/{slug}': get: summary: 'Show a credential type' operationId: showACredentialType description: "Returns the latest registered version of the credential type identified\nby its namespaced slug." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: object properties: slug: type: string examples: - 'revenexx:smtp' version: type: string examples: - 1.0.0 name: type: string examples: - SMTP description: type: string examples: - 'Send mail over SMTP' icon: type: - string - 'null' examples: - null images: type: array items: type: object properties: src: type: string examples: - 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: type: object properties: en: type: string examples: - 'SMTP logo' title: type: object properties: en: type: string examples: - SMTP category: type: string examples: - logo examples: - - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'SMTP logo' title: en: SMTP category: logo auth_kind: type: string examples: - secret fields: type: array examples: - [] examples: - data: slug: 'revenexx:smtp' version: 1.0.0 name: SMTP description: 'Send mail over SMTP' icon: null images: - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'SMTP logo' title: en: SMTP category: logo auth_kind: secret fields: [] 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Credential Types' parameters: - in: path name: slug description: 'Namespaced credential type slug.' required: true schema: type: string examples: - 'revenexx:smtp' /api/v1/credentials/oauth/callback: get: summary: 'OAuth redirect callback (public)' operationId: oAuthRedirectCallbackpublic description: "The provider redirects the user's browser here with `code` + `state`.\nUnauthenticated by necessity — the single-use `state` (looked up in the\ncache) is the CSRF protection and carries the tenant + PKCE verifier." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Connected content: application/json: schema: type: object properties: status: type: string examples: - connected credential_id: type: integer examples: - 1 examples: - status: connected credential_id: 1 400: description: 'Missing or invalid state' content: application/json: schema: type: object properties: message: type: string examples: - 'Invalid or expired OAuth state.' examples: - message: 'Invalid or expired OAuth state.' tags: - Credentials security: [] /api/v1/credentials: get: summary: 'List credentials' operationId: listCredentials description: "Returns the current tenant's credential instances, optionally filtered to\na single credential type via `?type={slug}`. Secrets are never exposed." parameters: - in: query name: type description: 'Filter by credential type slug.' required: false schema: type: string description: 'Filter by credential type slug.' examples: - 'revenexx:smtp' - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 1 tenant_id: type: string examples: - acme-production credential_type_slug: type: string examples: - 'revenexx:smtp' name: type: string examples: - 'Production SMTP' status: type: string examples: - active public_config: type: object properties: { } created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - - id: 1 tenant_id: acme-production credential_type_slug: 'revenexx:smtp' name: 'Production SMTP' status: active public_config: [] created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' examples: - data: - id: 1 tenant_id: acme-production credential_type_slug: 'revenexx:smtp' name: 'Production SMTP' status: active public_config: { } created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials post: summary: 'Create a credential' operationId: createACredential description: "Validates the submitted `config` against the credential type's declared\nfields, then stores it encrypted. Returns 201 with the masked instance." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 201: description: Created content: application/json: schema: type: object properties: id: type: integer examples: - 1 tenant_id: type: string examples: - acme-production credential_type_slug: type: string examples: - 'revenexx:smtp' name: type: string examples: - 'Production SMTP' status: type: string examples: - active public_config: type: object properties: { } created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - id: 1 tenant_id: acme-production credential_type_slug: 'revenexx:smtp' name: 'Production SMTP' status: active public_config: { } created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The given data was invalid.' errors: type: object properties: config: type: array items: type: string examples: - - ... examples: - message: 'The given data was invalid.' errors: config: - ... 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials requestBody: required: true content: application/json: schema: type: object properties: credential_type_slug: type: string description: 'Slug of a registered credential type the instance is created for. The slug of an existing record in the credential_types table.' examples: - 'revenexx:smtp' name: type: string description: 'Human-readable label, unique per tenant. Must not be greater than 191 characters.' examples: - Mailpit config: type: object description: 'Connection data for the credential type. The accepted fields are defined by the type manifest; stored encrypted and never returned.' properties: { } examples: - host: smtp.example.com port: 587 username: mailer password: sekret required: - credential_type_slug - name - config /api/v1/credentials/test: post: summary: 'Test an unsaved credential' operationId: testAnUnsavedCredential description: "Runs the broker connection test against an inline type + config, before\nthe instance is persisted. Non-blocking." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: '' content: application/json: schema: oneOf: - description: Tested type: object properties: ok: type: boolean description: 'Whether the values authenticate.' examples: - true message: type: string description: 'Why not, where the credential type said why — bounded to 500 characters, the same bound the stored reason on a saved credential is held to. Always present; null when there is nothing to say.' examples: - null examples: - ok: true message: null - description: Failed type: object properties: ok: type: boolean description: 'Whether the values authenticate.' examples: - false message: type: string description: 'Why not, where the credential type said why — bounded to 500 characters, the same bound the stored reason on a saved credential is held to. Always present; null when there is nothing to say.' examples: - 'Invalid auth key' examples: - ok: false message: 'Invalid auth key' 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials requestBody: required: true content: application/json: schema: type: object properties: credential_type_slug: type: string description: 'Slug of the credential type to test the inline config against. The slug of an existing record in the credential_types table.' examples: - 'revenexx:smtp' config: type: object description: "Inline connection data to exercise the type's connection test, before any instance is persisted." properties: { } examples: - host: smtp.example.com port: 587 username: mailer password: sekret required: - credential_type_slug - config '/api/v1/credentials/{id}': get: summary: 'Show a credential' operationId: showACredential description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: id: type: integer examples: - 1 tenant_id: type: string examples: - acme-production credential_type_slug: type: string examples: - 'revenexx:smtp' name: type: string examples: - 'Production SMTP' status: type: string examples: - active public_config: type: object properties: { } created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - id: 1 tenant_id: acme-production credential_type_slug: 'revenexx:smtp' name: 'Production SMTP' status: active public_config: { } created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials patch: summary: 'Update a credential' operationId: updateACredential description: "Updates the name and/or `config`. When `config` is supplied it is\nre-validated against the credential type's declared fields." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Updated content: application/json: schema: type: object properties: id: type: integer examples: - 1 tenant_id: type: string examples: - acme-production credential_type_slug: type: string examples: - 'revenexx:smtp' name: type: string examples: - 'Production SMTP' status: type: string examples: - active public_config: type: object properties: { } created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T11:00:00Z' examples: - id: 1 tenant_id: acme-production credential_type_slug: 'revenexx:smtp' name: 'Production SMTP' status: active public_config: { } created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T11:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The given data was invalid.' errors: type: object properties: config: type: array items: type: string examples: - - ... examples: - message: 'The given data was invalid.' errors: config: - ... 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'New human-readable label, unique per tenant. Optional. Must not be greater than 191 characters.' examples: - 'Mailpit (staging)' config: type: object description: 'Replacement connection data, re-validated against the credential type manifest. Optional; stored encrypted and never returned.' properties: { } examples: - host: smtp.example.com port: 587 username: mailer password: sekret delete: summary: 'Delete a credential' operationId: deleteACredential description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials parameters: - in: path name: id description: 'The ID of the credential.' required: true schema: type: string examples: - 9c1b4f2a-7d3e-4a6b-8c5d-1e2f3a4b5c6d '/api/v1/credentials/{id}/test': post: summary: 'Test a saved credential' operationId: testASavedCredential description: "Runs the credential type's connection test in the broker against the\nstored config and records the outcome on the instance (`last_test_at`,\n`last_test_ok`, `last_test_message`), so the answer to \"do these\ncredentials actually work?\" survives a reload — `status` cannot answer\nthat, it only tracks the OAuth refresh lifecycle. The stored config is\nnever modified." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Tested content: application/json: schema: type: object properties: ok: type: boolean examples: - true message: type: - string - 'null' examples: - null last_test_at: type: string examples: - '2026-07-16T21:00:00+00:00' last_test_ok: type: boolean examples: - true examples: - ok: true message: null last_test_at: '2026-07-16T21:00:00+00:00' last_test_ok: true 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials parameters: - in: path name: id description: 'The ID of the credential.' required: true schema: type: string examples: - 9c1b4f2a-7d3e-4a6b-8c5d-1e2f3a4b5c6d '/api/v1/credentials/{id}/oauth/authorize-url': post: summary: 'Begin 3-legged OAuth setup' operationId: begin3LeggedOAuthSetup description: "Returns the provider authorize URL for the credential. The caller (the\ncockpit) opens it; the provider redirects back to the public callback." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Authorize URL' content: application/json: schema: type: object properties: authorize_url: type: string examples: - 'https://login.example.com/authorize?client_id=...&state=...' examples: - authorize_url: 'https://login.example.com/authorize?client_id=...&state=...' 422: description: 'Not a 3-legged OAuth type' content: application/json: schema: type: object properties: message: type: string examples: - 'Credential type does not use 3-legged OAuth.' examples: - message: 'Credential type does not use 3-legged OAuth.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Credentials parameters: - in: path name: id description: 'The ID of the credential.' required: true schema: type: string examples: - 9c1b4f2a-7d3e-4a6b-8c5d-1e2f3a4b5c6d '/api/v1/node-packages/images/{id}': get: summary: 'Stream a node-package image.' operationId: streamANodePackageImage description: '' parameters: - in: query name: expires description: 'Signed-URL expiry (added by the signer).' required: true schema: type: integer description: 'Signed-URL expiry (added by the signer).' examples: - 16 - in: query name: signature description: 'URL signature (added by the signer).' required: true schema: type: string description: 'URL signature (added by the signer).' examples: - architecto - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Valid signature' content: application/json: schema: type: string examples: - '' 403: description: 'Missing, tampered, or expired signature' content: application/json: schema: type: object properties: message: type: string examples: - 'Invalid signature.' examples: - message: 'Invalid signature.' 404: description: 'Unknown image, or stored file missing' content: application/json: schema: type: object properties: message: type: string examples: - 'Not Found' examples: - message: 'Not Found' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Node Packages' parameters: - in: path name: id description: 'The ID of the image.' required: true schema: type: string examples: - architecto - in: path name: image description: 'The image UUID.' required: true schema: type: string examples: - 9b2e6b1e-6d0a-5f3a-8c1b-2b7f6a1c9d4e /api/v1/nodes: get: summary: 'List nodes' operationId: listNodes description: "Returns the registered nodes ordered by `slug` ascending, then\nsemver descending. Optionally filters to a single namespace via the\n`namespace` query parameter." parameters: - in: query name: namespace description: 'Filter by the slug-derived namespace (e.g. `revenexx`).' required: false schema: type: string description: 'Filter by the slug-derived namespace (e.g. `revenexx`).' examples: - revenexx - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: name: type: string examples: - 'HTTP request' namespace: type: string examples: - revenexx slug: type: string examples: - 'revenexx:http-request' version: type: string examples: - 1.0.0 package: type: object properties: name: type: string examples: - '@revenexx/integrations-nodes-core' version: type: string examples: - 0.2.0 label: type: string examples: - Core manifest_version: type: string examples: - v0-draft manifest: type: object properties: { } created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - - name: 'HTTP request' namespace: revenexx slug: 'revenexx:http-request' version: 1.0.0 package: name: '@revenexx/integrations-nodes-core' version: 0.2.0 label: Core manifest_version: v0-draft manifest: [] created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' examples: - data: - name: 'HTTP request' namespace: revenexx slug: 'revenexx:http-request' version: 1.0.0 package: name: '@revenexx/integrations-nodes-core' version: 0.2.0 label: Core manifest_version: v0-draft manifest: { } created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 429: $ref: '#/components/responses/TooManyRequests' tags: - Nodes '/api/v1/nodes/{slug}/versions': get: summary: 'List versions for a node' operationId: listVersionsForANode description: "Returns the registered versions for a given slug, semver-descending.\nThe slug must match at least one stored row, otherwise a 404 is\nreturned — there's no representation for \"an empty list of\nversions\". The response intentionally only contains version\nstrings; callers that need the full row should follow up with\n`GET /nodes/{slug}/{version}`." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: string examples: - - 2.5.0 - 1.10.0 - 1.0.0 examples: - data: - 2.5.0 - 1.10.0 - 1.0.0 404: description: 'Unknown slug' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Nodes parameters: - in: path name: slug description: 'Full node slug.' required: true schema: type: string examples: - 'revenexx:http-request' '/api/v1/nodes/{slug}/{version}': get: summary: 'Show a node' operationId: showANode description: "Returns a single node by `(slug, version)`. The version may be the\nliteral string `latest`, in which case the highest semver registered\nfor the slug is returned." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: name: type: string examples: - 'HTTP request' namespace: type: string examples: - revenexx slug: type: string examples: - 'revenexx:http-request' version: type: string examples: - 1.0.0 package: type: object properties: name: type: string examples: - '@revenexx/integrations-nodes-core' version: type: string examples: - 0.2.0 label: type: string examples: - Core manifest_version: type: string examples: - v0-draft manifest: type: object properties: { } created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - name: 'HTTP request' namespace: revenexx slug: 'revenexx:http-request' version: 1.0.0 package: name: '@revenexx/integrations-nodes-core' version: 0.2.0 label: Core manifest_version: v0-draft manifest: { } created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Nodes delete: summary: 'Delete a node' operationId: deleteANode description: "Removes the node version identified by `(slug, version)`. The\n`latest` alias is rejected to prevent ambiguous deletes." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 400: description: 'Latest alias rejected' content: application/json: schema: type: object properties: message: type: string examples: - 'The `latest` alias is not allowed for mutating requests.' examples: - message: 'The `latest` alias is not allowed for mutating requests.' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Nodes parameters: - in: path name: slug description: 'Full node slug.' required: true schema: type: string examples: - 'revenexx:http-request' - in: path name: version description: 'Semver, or the alias `latest`.' required: true schema: type: string examples: - 1.0.0 '/api/v1/nodes/{slug}/{version}/config:resolve': post: summary: 'Resolve dynamic node config' operationId: resolveDynamicNodeConfig description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Resolved content: application/json: schema: type: object properties: fields: type: array items: type: object properties: key: type: string examples: - customerId type: type: string examples: - string examples: - - key: customerId type: string outputs: type: array items: type: object properties: name: type: string examples: - success examples: - - name: success examples: - fields: - key: customerId type: string outputs: - name: success 404: description: 'Unknown node' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Node version has no built code' content: application/json: schema: type: object properties: message: type: string examples: - 'This node version has no built code, so it cannot be executed. Its code is built when its package is registered.' examples: - message: 'This node version has no built code, so it cannot be executed. Its code is built when its package is registered.' 502: description: 'Node runtime unavailable or failed' content: application/json: schema: type: object properties: message: type: string examples: - 'Node runtime resolve failed: ...' examples: - message: 'Node runtime resolve failed: ...' 429: $ref: '#/components/responses/TooManyRequests' tags: - Nodes requestBody: required: true content: application/json: schema: type: object properties: target: type: string description: '"outputs", "*" (all), or a config field key. Must not be greater than 128 characters.' examples: - '*' config: type: object description: "The user's current partial config values." properties: { } examples: - customerId: '42' locale: type: - string - 'null' description: 'Preferred locale for resolved labels. Must not be greater than 16 characters.' examples: - de search: type: - string - 'null' description: "The operator's type-to-search term for the option list (config-field targets only). Must not be greater than 200 characters." examples: - acme required: - target parameters: - in: path name: slug description: 'Full node slug.' required: true schema: type: string examples: - 'revenexx:api' - in: path name: version description: 'Semver, or the alias `latest`.' required: true schema: type: string examples: - 1.0.0 '/api/v1/nodes/{slug}/{version}/execute:test': post: summary: 'Test-execute a node' operationId: testExecuteANode description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Executed content: application/json: schema: type: object properties: outputs: type: object properties: customer: type: object properties: id: type: string examples: - '42' branch: type: string examples: - success logs: type: array items: type: object properties: level: type: string examples: - info message: type: string examples: - 'fetched customer 42' examples: - - level: info message: 'fetched customer 42' examples: - outputs: customer: id: '42' branch: success logs: - level: info message: 'fetched customer 42' 404: description: 'Unknown node' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Node version has no built code' content: application/json: schema: type: object properties: message: type: string examples: - 'This node version has no built code, so it cannot be executed. Its code is built when its package is registered.' examples: - message: 'This node version has no built code, so it cannot be executed. Its code is built when its package is registered.' 422: description: 'Node threw' content: application/json: schema: type: object properties: error: type: object properties: message: type: string examples: - 'rate limited' code: type: string examples: - RATE_LIMITED logs: type: array examples: - [] examples: - error: message: 'rate limited' code: RATE_LIMITED logs: [] 502: description: 'Node runtime unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Node runtime execute failed: ...' examples: - message: 'Node runtime execute failed: ...' 429: $ref: '#/components/responses/TooManyRequests' tags: - Nodes requestBody: required: false content: application/json: schema: type: object properties: config: type: object description: "The node's config values to run with." properties: { } examples: - customerId: '42' inputs: type: object description: 'Simulated input payloads, merged over the config.' properties: { } examples: - payload: email: test@acme.com timeout_ms: type: integer description: 'Wall-clock limit in milliseconds; clamped by the runtime. Must be at least 1000.' examples: - 30000 workflow_id: type: integer description: "The workflow being edited. Grants the test run read-only access to that workflow's declared state namespaces; writes stay run-only." examples: - 12 parameters: - in: path name: slug description: 'Full node slug.' required: true schema: type: string examples: - 'revenexx:api' - in: path name: version description: 'Semver, or the alias `latest`.' required: true schema: type: string examples: - 1.0.0 '/api/v1/workflows/{workflowId}/dead-letters': get: summary: 'List dead-lettered firings for a workflow' operationId: listDeadLetteredFiringsForAWorkflow description: "Newest first. Only firings for the addressed workflow, scoped to the\nauthenticated tenant." parameters: - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Number of dead letters per page (1–100). Defaults to 25. Must be at least 1. Must not be greater than 100.' required: false schema: type: - integer - 'null' description: 'Number of dead letters per page (1–100). Defaults to 25. Must be at least 1. Must not be greater than 100.' examples: - 25 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array examples: - [] meta: type: object properties: current_page: type: integer examples: - 1 per_page: type: integer examples: - 25 total: type: integer examples: - 0 last_page: type: integer examples: - 1 examples: - data: [] meta: current_page: 1 per_page: 25 total: 0 last_page: 1 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Run Dead Letters' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 /api/v1/dead-letters: get: summary: "List the tenant's missed firings" operationId: listTheTenantsMissedFirings description: "Every trigger firing across the tenant that never became a run, newest\nfirst. Optionally narrowed by handling state, by workflow and by a time\nwindow.\n\nThe sibling of `GET /v1/runs`: a firing that produced a run belongs\nthere, one that produced none belongs here. Each row names the workflow\nit belongs to, which the per-workflow list can leave to the request path\nand this one cannot.\n\nNo total is reported — `GET /v1/dead-letters/summary` answers \"how many\",\nincluding the count of those still waiting." parameters: - in: query name: status description: 'Restrict to firings in these handling states. One value, or several comma-separated (pending, replayed, discarded).' required: false schema: type: array description: 'Restrict to firings in these handling states. One value, or several comma-separated (pending, replayed, discarded).' items: type: string enum: - pending - replayed - discarded examples: - - pending - in: query name: workflow_id description: 'Restrict to firings of one workflow. A workflow of another tenant is rejected. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Restrict to firings of one workflow. A workflow of another tenant is rejected. Must be at least 1.' examples: - 1 - in: query name: since description: 'Only firings captured at or after this instant. Must be a valid date.' required: false schema: type: - string - 'null' description: 'Only firings captured at or after this instant. Must be a valid date.' examples: - '2026-08-01T00:00:00Z' - in: query name: until description: 'Only firings captured at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' required: false schema: type: - string - 'null' description: 'Only firings captured at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' examples: - '2026-08-21T00:00:00Z' - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Firings per page. Clamped to the configured maximum (100) rather than rejected.' required: false schema: type: - integer - 'null' description: 'Firings per page. Clamped to the configured maximum (100) rather than rejected.' examples: - 25 - in: query name: search description: '' example: null required: false schema: type: string description: '' examples: - null - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 0: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 7 workflow_id: type: integer examples: - 1 workflow_name: type: string description: 'The name of the workflow whose firing was missed.' examples: - 'Sync orders' trigger_id: type: integer examples: - 3 source: type: string examples: - schedule trigger_handle: type: string examples: - 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c reason: type: string description: "Why the firing never became a run, in the platform's words. Empty when nothing was recorded." examples: - 'Workflow bundle not ready (build_' status: type: string examples: - pending created_at: type: string examples: - '2026-08-21T02:00:00Z' updated_at: type: string examples: - '2026-08-21T02:00:00Z' required: - workflow_name - reason examples: - - id: 7 workflow_id: 1 workflow_name: 'Sync orders' trigger_id: 3 source: schedule trigger_handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c reason: 'Workflow bundle not ready (build_' status: pending created_at: '2026-08-21T02:00:00Z' updated_at: '2026-08-21T02:00:00Z' meta: type: object properties: current_page: type: integer examples: - 1 per_page: type: integer examples: - 25 has_more: type: boolean description: 'Whether a further page exists. This list reports no total — see GET /v1/dead-letters/summary.' examples: - false required: - has_more examples: - data: - id: 7 workflow_id: 1 workflow_name: 'Sync orders' trigger_id: 3 source: schedule trigger_handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c reason: 'Workflow bundle not ready (build_' status: pending created_at: '2026-08-21T02:00:00Z' updated_at: '2026-08-21T02:00:00Z' meta: current_page: 1 per_page: 25 has_more: false 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 403: description: 'No tenant context' content: application/json: schema: type: object properties: message: type: string examples: - 'Tenant context is required.' examples: - message: 'Tenant context is required.' 500: description: '' content: application/json: schema: type: object properties: message: type: string examples: - 'Server Error' examples: - message: 'Server Error' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Run Dead Letters' /api/v1/dead-letters/summary: get: summary: "Count the tenant's missed firings" operationId: countTheTenantsMissedFirings description: "How many firings never became runs, broken down by handling state, under\nexactly the narrowing `GET /v1/dead-letters` accepts.\n\n`by_status.pending` is the count the studio shows on its Missed runs tab\nand on the dashboard: the firings still waiting to be sent through or put\naside. Every state appears, zeros included, so \"none waiting\" cannot read\nas \"not loaded yet\"." parameters: - in: query name: status description: 'Restrict to firings in these handling states. One value, or several comma-separated (pending, replayed, discarded).' required: false schema: type: array description: 'Restrict to firings in these handling states. One value, or several comma-separated (pending, replayed, discarded).' items: type: string enum: - pending - replayed - discarded examples: - - pending - in: query name: workflow_id description: 'Restrict to firings of one workflow. A workflow of another tenant is rejected. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Restrict to firings of one workflow. A workflow of another tenant is rejected. Must be at least 1.' examples: - 1 - in: query name: since description: 'Only firings captured at or after this instant. Must be a valid date.' required: false schema: type: - string - 'null' description: 'Only firings captured at or after this instant. Must be a valid date.' examples: - '2026-08-01T00:00:00Z' - in: query name: until description: 'Only firings captured at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' required: false schema: type: - string - 'null' description: 'Only firings captured at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' examples: - '2026-08-21T00:00:00Z' - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Firings per page. Clamped to the configured maximum (100) rather than rejected.' required: false schema: type: - integer - 'null' description: 'Firings per page. Clamped to the configured maximum (100) rather than rejected.' examples: - 25 - in: query name: search description: '' example: null required: false schema: type: string description: '' examples: - null - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: object properties: total: type: integer description: 'How many missed firings match the narrowing.' examples: - 7 by_status: type: object properties: pending: type: integer examples: - 4 replayed: type: integer examples: - 2 discarded: type: integer examples: - 1 description: 'A count per handling state. Every state appears, including the ones at zero.' required: - total - by_status examples: - data: total: 7 by_status: pending: 4 replayed: 2 discarded: 1 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 403: description: 'No tenant context' content: application/json: schema: type: object properties: message: type: string examples: - 'Tenant context is required.' examples: - message: 'Tenant context is required.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Run Dead Letters' '/api/v1/dead-letters/{id}/replay': post: summary: 'Replay a dead-lettered firing' operationId: replayADeadLetteredFiring description: "Starts a fresh run from the captured trigger payload + handle. Requires\nthe workflow to be runnable again; marks the dead letter `replayed`." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 0: description: 'Workflow not runnable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow bundle is not ready (build_' examples: - message: 'Workflow bundle is not ready (build_' 202: description: Accepted content: application/json: schema: type: object properties: id: type: integer examples: - 5 workflow_id: type: integer examples: - 1 status: type: string examples: - running examples: - id: 5 workflow_id: 1 status: running 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Already handled' content: application/json: schema: type: object properties: message: type: string examples: - 'Dead letter has already been replayed or discarded.' examples: - message: 'Dead letter has already been replayed or discarded.' 422: description: 'Trigger no longer resolvable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow has no active manual trigger; pass trigger_handle to start a specific trigger.' examples: - message: 'Workflow has no active manual trigger; pass trigger_handle to start a specific trigger.' 503: description: 'Worker unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow worker is unavailable.' examples: - message: 'Workflow worker is unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Run Dead Letters' parameters: - in: path name: id description: 'The dead letter id.' required: true schema: type: integer examples: - 1 '/api/v1/dead-letters/{id}/discard': post: summary: 'Discard a dead-lettered firing' operationId: discardADeadLetteredFiring description: 'Marks the dead letter `discarded` without starting a run.' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Discarded content: application/json: schema: type: object properties: id: type: integer examples: - 1 status: type: string examples: - discarded examples: - id: 1 status: discarded 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Already handled' content: application/json: schema: type: object properties: message: type: string examples: - 'Dead letter has already been replayed or discarded.' examples: - message: 'Dead letter has already been replayed or discarded.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Run Dead Letters' parameters: - in: path name: id description: 'The dead letter id.' required: true schema: type: integer examples: - 1 /api/v1/runs: get: summary: "List the tenant's runs" operationId: listTheTenantsRuns description: "Newest first, across every workflow the tenant owns, and optionally in\nanother order — by when a run started, or by how long it took. Optionally\nnarrowed by outcome, by the kind of trigger that\nstarted the run, by workflow, and by a time window; the filters narrow\ntogether, and the order applies to what they left.\n\nThe rows are leaner than the per-workflow list's: a run's `result` and\ncaptured trigger payload are not included, because they can run to\nmegabytes each. Read them from `GET /workflows/{workflowId}/runs/{runId}`.\n\nNo total is reported. `GET /v1/runs/summary` answers \"how many\", and\nanswers it under the same narrowing — a page cannot, and pretending\notherwise is what made clients read every page to count." parameters: - in: query name: status description: 'Restrict to runs in these statuses. One value, or several comma-separated (pending, running, completed, failed, cancelled, terminated).' required: false schema: type: array description: 'Restrict to runs in these statuses. One value, or several comma-separated (pending, running, completed, failed, cancelled, terminated).' items: type: string enum: - pending - running - completed - failed - cancelled - terminated examples: - - failed - in: query name: workflow_id description: 'Restrict to runs of one workflow. A workflow of another tenant is rejected. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Restrict to runs of one workflow. A workflow of another tenant is rejected. Must be at least 1.' examples: - 1 - in: query name: since description: 'Only runs created at or after this instant. Must be a valid date.' required: false schema: type: - string - 'null' description: 'Only runs created at or after this instant. Must be a valid date.' examples: - '2026-08-01T00:00:00Z' - in: query name: until description: 'Only runs created at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' required: false schema: type: - string - 'null' description: 'Only runs created at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' examples: - '2026-08-21T00:00:00Z' - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Runs per page. Clamped to the configured maximum (100) rather than rejected.' required: false schema: type: - integer - 'null' description: 'Runs per page. Clamped to the configured maximum (100) rather than rejected.' examples: - 25 - in: query name: trigger_type description: 'Restrict to runs started by this kind of trigger (manual, schedule, webhook, event).' required: false schema: type: - string - 'null' description: 'Restrict to runs started by this kind of trigger (manual, schedule, webhook, event).' enum: - manual - schedule - webhook - event examples: - schedule - in: query name: search description: 'optional Narrow to runs of workflows whose name contains this text (case-insensitive).' required: false schema: type: - string - 'null' description: 'optional Narrow to runs of workflows whose name contains this text (case-insensitive).' examples: - orders - in: query name: sort description: 'Which order to read the list in (created_at, started_at, duration). Defaults to `created_at`. `duration` is the time between a run starting and finishing. A run the chosen column cannot describe — one that never started, or one still running — is listed last whichever direction is asked for. Ordering is always broken by the run id, so paging never repeats or loses a row.' required: false schema: type: - string - 'null' description: 'Which order to read the list in (created_at, started_at, duration). Defaults to `created_at`. `duration` is the time between a run starting and finishing. A run the chosen column cannot describe — one that never started, or one still running — is listed last whichever direction is asked for. Ordering is always broken by the run id, so paging never repeats or loses a row.' enum: - created_at - started_at - duration examples: - duration - in: query name: direction description: 'Which way to read the order: `desc` (the default) or `asc`.' required: false schema: type: - string - 'null' description: 'Which way to read the order: `desc` (the default) or `asc`.' enum: - asc - desc examples: - desc - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 42 workflow_id: type: integer examples: - 1 workflow_name: type: string description: "The name of the workflow the run belongs to. Always present — a run's workflow cannot be missing." examples: - 'Sync orders' status: type: string examples: - failed error: type: - string - 'null' description: "Why the run failed, in the platform's words; null for a run that did not fail." examples: - 'Node revenexx:http-request failed: 500' trigger_handle: type: string examples: - 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c trigger_type: type: - string - 'null' description: 'What kind of trigger started the run (manual, schedule, webhook, event). Null when that trigger has since been deleted, or when the run predates the platform recording it.' examples: - schedule temporal_workflow_id: type: string examples: - 'acme-production:1:42' temporal_run_id: type: string examples: - 018f... started_at: type: string examples: - '2026-08-21T02:00:00Z' completed_at: type: string examples: - '2026-08-21T02:00:07Z' created_at: type: string examples: - '2026-08-21T02:00:00Z' updated_at: type: string examples: - '2026-08-21T02:00:07Z' required: - workflow_name examples: - - id: 42 workflow_id: 1 workflow_name: 'Sync orders' status: failed error: 'Node revenexx:http-request failed: 500' trigger_handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c trigger_type: schedule temporal_workflow_id: 'acme-production:1:42' temporal_run_id: 018f... started_at: '2026-08-21T02:00:00Z' completed_at: '2026-08-21T02:00:07Z' created_at: '2026-08-21T02:00:00Z' updated_at: '2026-08-21T02:00:07Z' meta: type: object properties: current_page: type: integer examples: - 1 per_page: type: integer examples: - 25 has_more: type: boolean description: 'Whether a further page exists. This list reports no total — see GET /v1/runs/summary.' examples: - false required: - has_more examples: - data: - id: 42 workflow_id: 1 workflow_name: 'Sync orders' status: failed error: 'Node revenexx:http-request failed: 500' trigger_handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c trigger_type: schedule temporal_workflow_id: 'acme-production:1:42' temporal_run_id: 018f... started_at: '2026-08-21T02:00:00Z' completed_at: '2026-08-21T02:00:07Z' created_at: '2026-08-21T02:00:00Z' updated_at: '2026-08-21T02:00:07Z' meta: current_page: 1 per_page: 25 has_more: false 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 403: description: 'No tenant context' content: application/json: schema: type: object properties: message: type: string examples: - 'Tenant context is required.' examples: - message: 'Tenant context is required.' 422: description: 'Unknown status' content: application/json: schema: type: object properties: message: type: string examples: - 'The given data was invalid.' errors: type: object properties: status.0: type: array items: type: string examples: - - 'The selected status.0 is invalid.' examples: - message: 'The given data was invalid.' errors: status.0: - 'The selected status.0 is invalid.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Runs /api/v1/runs/summary: get: summary: "Count the tenant's runs" operationId: countTheTenantsRuns description: "How many runs the tenant has, broken down by outcome, under exactly the\nnarrowing `GET /v1/runs` accepts — so a figure and the list beside it can\nnever disagree.\n\nSeparate from the list on purpose. Coupling \"how many\" to \"which page\" is\nwhat made clients read every page to count, and a tenant-wide COUNT on a\nlist that refreshes itself every few seconds is the most expensive query\nthis surface could ask for. Asked on its own, it can be asked less often.\n\nEvery status is present, zeros included. An absent key would leave \"none\nof those\" and \"not loaded\" reading alike, which is the confusion these\nfigures exist to remove." parameters: - in: query name: status description: 'Restrict to runs in these statuses. One value, or several comma-separated (pending, running, completed, failed, cancelled, terminated).' required: false schema: type: array description: 'Restrict to runs in these statuses. One value, or several comma-separated (pending, running, completed, failed, cancelled, terminated).' items: type: string enum: - pending - running - completed - failed - cancelled - terminated examples: - - failed - in: query name: workflow_id description: 'Restrict to runs of one workflow. A workflow of another tenant is rejected. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Restrict to runs of one workflow. A workflow of another tenant is rejected. Must be at least 1.' examples: - 1 - in: query name: since description: 'Only runs created at or after this instant. Must be a valid date.' required: false schema: type: - string - 'null' description: 'Only runs created at or after this instant. Must be a valid date.' examples: - '2026-08-01T00:00:00Z' - in: query name: until description: 'Only runs created at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' required: false schema: type: - string - 'null' description: 'Only runs created at or before this instant. Must not precede `since`. Must be a valid date. Must be a date after or equal to since.' examples: - '2026-08-21T00:00:00Z' - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Runs per page. Clamped to the configured maximum (100) rather than rejected.' required: false schema: type: - integer - 'null' description: 'Runs per page. Clamped to the configured maximum (100) rather than rejected.' examples: - 25 - in: query name: trigger_type description: 'Restrict to runs started by this kind of trigger (manual, schedule, webhook, event).' required: false schema: type: - string - 'null' description: 'Restrict to runs started by this kind of trigger (manual, schedule, webhook, event).' enum: - manual - schedule - webhook - event examples: - schedule - in: query name: search description: "Restrict to runs whose workflow's name contains this text (case-insensitive). Must not be greater than 200 characters." required: false schema: type: - string - 'null' description: "Restrict to runs whose workflow's name contains this text (case-insensitive). Must not be greater than 200 characters." examples: - orders - in: query name: sort description: 'Which order to read the list in (created_at, started_at, duration). Defaults to `created_at`. `duration` is the time between a run starting and finishing. A run the chosen column cannot describe — one that never started, or one still running — is listed last whichever direction is asked for. Ordering is always broken by the run id, so paging never repeats or loses a row.' required: false schema: type: - string - 'null' description: 'Which order to read the list in (created_at, started_at, duration). Defaults to `created_at`. `duration` is the time between a run starting and finishing. A run the chosen column cannot describe — one that never started, or one still running — is listed last whichever direction is asked for. Ordering is always broken by the run id, so paging never repeats or loses a row.' enum: - created_at - started_at - duration examples: - duration - in: query name: direction description: 'Which way to read the order: `desc` (the default) or `asc`.' required: false schema: type: - string - 'null' description: 'Which way to read the order: `desc` (the default) or `asc`.' enum: - asc - desc examples: - desc - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: object properties: total: type: integer description: 'How many runs match the narrowing.' examples: - 124 in_flight: type: integer description: 'How many are still expected to finish (pending + running), derived here so every surface counts it the same way.' examples: - 3 by_status: type: object properties: pending: type: integer examples: - 1 running: type: integer examples: - 2 completed: type: integer examples: - 100 failed: type: integer examples: - 19 cancelled: type: integer examples: - 1 terminated: type: integer examples: - 1 description: 'A count per run status. Every status appears, including the ones at zero.' required: - total - in_flight - by_status examples: - data: total: 124 in_flight: 3 by_status: pending: 1 running: 2 completed: 100 failed: 19 cancelled: 1 terminated: 1 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 403: description: 'No tenant context' content: application/json: schema: type: object properties: message: type: string examples: - 'Tenant context is required.' examples: - message: 'Tenant context is required.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Runs /api/v1/runs/latest-per-workflow: get: summary: 'The latest run of each workflow' operationId: theLatestRunOfEachWorkflow description: "One row per workflow that has ever run — its most recent run — newest\nworkflow first. Workflows that have never run are absent rather than\npresent-and-empty.\n\nIts own endpoint because it is a different question from the feed, not a\npage of it. `specs/workflows.md` AC-4 promises a row says how its workflow\nlast ran, and that a workflow which never ran claims no outcome at all —\nso reading it off the first page of the tenant's runs would make a\nworkflow whose last run happens to be older than a hundred others claim it\nhad never run, which is the mirror of the lie that criterion forbids.\n\nUnpaged: there is at most one row per workflow, the same bound\n`GET /v1/workflows` already lives with." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 42 workflow_id: type: integer examples: - 1 workflow_name: type: string examples: - 'Sync orders' status: type: string examples: - failed error: type: string examples: - 'Node revenexx:http-request failed: 500' trigger_handle: type: string examples: - 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c trigger_type: type: string examples: - schedule temporal_workflow_id: type: string examples: - 'acme-production:1:42' temporal_run_id: type: string examples: - 018f... started_at: type: string examples: - '2026-08-21T02:00:00Z' completed_at: type: string examples: - '2026-08-21T02:00:07Z' created_at: type: string examples: - '2026-08-21T02:00:00Z' updated_at: type: string examples: - '2026-08-21T02:00:07Z' examples: - - id: 42 workflow_id: 1 workflow_name: 'Sync orders' status: failed error: 'Node revenexx:http-request failed: 500' trigger_handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c trigger_type: schedule temporal_workflow_id: 'acme-production:1:42' temporal_run_id: 018f... started_at: '2026-08-21T02:00:00Z' completed_at: '2026-08-21T02:00:07Z' created_at: '2026-08-21T02:00:00Z' updated_at: '2026-08-21T02:00:07Z' examples: - data: - id: 42 workflow_id: 1 workflow_name: 'Sync orders' status: failed error: 'Node revenexx:http-request failed: 500' trigger_handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c trigger_type: schedule temporal_workflow_id: 'acme-production:1:42' temporal_run_id: 018f... started_at: '2026-08-21T02:00:00Z' completed_at: '2026-08-21T02:00:07Z' created_at: '2026-08-21T02:00:00Z' updated_at: '2026-08-21T02:00:07Z' 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 403: description: 'No tenant context' content: application/json: schema: type: object properties: message: type: string examples: - 'Tenant context is required.' examples: - message: 'Tenant context is required.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Runs /api/v1/schedules: get: summary: "List the tenant's schedules" operationId: listTheTenantsSchedules description: "Every `schedule` trigger across every workflow, and nothing else — a\nwebhook or a manual trigger is not a schedule and is not listed.\n\nServed whole rather than paged, like the tenant's workflows and\ncredentials: there is one row per schedule, the page that shows them\nnarrows and sorts them in the browser, and a count of what is live is only\ntrue if it covers all of them. `meta` therefore describes the whole\ntenant, not a page." parameters: - in: query name: active description: 'Restrict to live (`true`) or paused (`false`) schedules.' required: false schema: type: - boolean - 'null' description: 'Restrict to live (`true`) or paused (`false`) schedules.' examples: - true - in: query name: timezone description: 'Restrict to schedules that run in this timezone. Matches the effective zone, so the platform default also matches schedules that carry none. The zones actually in use are reported as `meta.timezones`.' required: false schema: type: - string - 'null' description: 'Restrict to schedules that run in this timezone. Matches the effective zone, so the platform default also matches schedules that carry none. The zones actually in use are reported as `meta.timezones`.' examples: - Europe/Berlin - in: query name: next_fire_count description: 'How many upcoming firings each schedule reports (1–10). Defaults to 1. Must be at least 1. Must not be greater than 10.' required: false schema: type: - integer - 'null' description: 'How many upcoming firings each schedule reports (1–10). Defaults to 1. Must be at least 1. Must not be greater than 10.' examples: - 1 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 13 workflow_id: type: integer examples: - 1 workflow_name: type: string description: 'The workflow the schedule belongs to.' examples: - 'Sync orders' handle: type: string examples: - 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c name: type: - string - 'null' examples: - null active: type: boolean description: 'Whether the schedule itself is live. Distinct from `workflow_active`: a live schedule on a deactivated workflow does not fire, and `schedule.not_firing_reason` says which of the two is the cause.' examples: - true workflow_active: type: boolean description: 'Whether the workflow the schedule belongs to is active.' examples: - true schedule: type: object properties: cron: type: string examples: - '0 3 * * *' timezone: type: string examples: - Europe/Berlin timezone_source: type: string examples: - schedule holds_firings: type: boolean examples: - true not_firing_reason: type: - string - 'null' examples: - null next_fire_at: type: string examples: - '2026-08-22T01:00:00Z' next_fire_times: type: array items: type: object properties: at: type: string examples: - '2026-08-22T01:00:00Z' local: type: string examples: - '2026-08-22T03:00:00+02:00' examples: - - at: '2026-08-22T01:00:00Z' local: '2026-08-22T03:00:00+02:00' last_dispatch_at: type: string examples: - '2026-08-21T01:00:00Z' last_run: type: object properties: id: type: integer examples: - 4711 status: type: string examples: - completed error: type: - string - 'null' examples: - null started_at: type: string examples: - '2026-08-21T01:00:01Z' completed_at: type: string examples: - '2026-08-21T01:00:44Z' description: 'The firings the platform holds and the last run it performed — the same block the per-workflow trigger list returns.' created_at: type: string examples: - '2026-06-15T10:00:00Z' updated_at: type: string examples: - '2026-06-15T10:00:00Z' required: - workflow_name - active - workflow_active - schedule examples: - - id: 13 workflow_id: 1 workflow_name: 'Sync orders' handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c name: null active: true workflow_active: true schedule: cron: '0 3 * * *' timezone: Europe/Berlin timezone_source: schedule holds_firings: true not_firing_reason: null next_fire_at: '2026-08-22T01:00:00Z' next_fire_times: - at: '2026-08-22T01:00:00Z' local: '2026-08-22T03:00:00+02:00' last_dispatch_at: '2026-08-21T01:00:00Z' last_run: id: 4711 status: completed error: null started_at: '2026-08-21T01:00:01Z' completed_at: '2026-08-21T01:00:44Z' created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' meta: type: object properties: total: type: integer examples: - 1 live_count: type: integer description: "How many of the tenant's schedules are live, counted over the tenant rather than the response." examples: - 1 timezones: type: array description: "The effective timezones the tenant's schedules actually run in, sorted. A schedule carrying none contributes the platform default." items: type: string examples: - - Europe/Berlin required: - live_count - timezones examples: - data: - id: 13 workflow_id: 1 workflow_name: 'Sync orders' handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c name: null active: true workflow_active: true schedule: cron: '0 3 * * *' timezone: Europe/Berlin timezone_source: schedule holds_firings: true not_firing_reason: null next_fire_at: '2026-08-22T01:00:00Z' next_fire_times: - at: '2026-08-22T01:00:00Z' local: '2026-08-22T03:00:00+02:00' last_dispatch_at: '2026-08-21T01:00:00Z' last_run: id: 4711 status: completed error: null started_at: '2026-08-21T01:00:01Z' completed_at: '2026-08-21T01:00:44Z' created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' meta: total: 1 live_count: 1 timezones: - Europe/Berlin 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 403: description: 'No tenant context' content: application/json: schema: type: object properties: message: type: string examples: - 'Tenant context is required.' examples: - message: 'Tenant context is required.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Schedules '/api/v1/schemas/{domain}': get: summary: 'List schema versions' operationId: listSchemaVersions description: "Returns the identifiers of every registered schema version for the given\ndomain, in registration order (which is also the order in which they\nare accepted by the validation layer)." parameters: [] responses: 200: description: Success content: application/json: schema: type: object properties: domain: type: string examples: - workflow versions: type: array items: type: string examples: - - v0-draft examples: - domain: workflow versions: - v0-draft 404: description: 'Unknown domain' content: application/json: schema: type: object properties: message: type: string examples: - 'Unknown schema domain.' examples: - message: 'Unknown schema domain.' tags: - Schemas security: [] parameters: - in: path name: domain description: 'The schema domain. Supported domains: `workflow`, `node`.' required: true schema: type: string examples: - workflow '/api/v1/schemas/{domain}/{version}': get: summary: 'Show a schema version' operationId: showASchemaVersion description: "Returns the serialized JSON-Schema definition for one specific\n`(domain, version)` pair. The payload uses the standard JSON Schema\nvocabulary (`type`, `properties`, `required`, …) and is suitable for\nclient-side validation or form generation." parameters: [] responses: 200: description: Success content: application/json: schema: type: object properties: domain: type: string examples: - workflow version: type: string examples: - v0-draft schema: type: object properties: title: type: string examples: - 'Workflow Definition (v0-draft)' type: type: string examples: - object examples: - domain: workflow version: v0-draft schema: title: 'Workflow Definition (v0-draft)' type: object 404: description: 'Unknown version' content: application/json: schema: type: object properties: message: type: string examples: - 'Unknown schema version.' examples: - message: 'Unknown schema version.' tags: - Schemas security: [] parameters: - in: path name: domain description: 'The schema domain.' required: true schema: type: string examples: - workflow - in: path name: version description: 'The schema version identifier.' required: true schema: type: string examples: - v0-draft /api/v1/state/namespaces: get: summary: 'List state namespaces' operationId: listStateNamespaces description: 'Every namespace in the current tenant, with its entry count.' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 1 name: type: string examples: - article role: type: string examples: - mapping visibility: type: string examples: - shared owner_workflow_id: type: - string - 'null' examples: - null entries: type: integer examples: - 2 ttl_seconds: type: - string - 'null' examples: - null updated_at: type: string examples: - '2026-08-26T10:00:00Z' examples: - - id: 1 name: article role: mapping visibility: shared owner_workflow_id: null entries: 2 ttl_seconds: null updated_at: '2026-08-26T10:00:00Z' examples: - data: - id: 1 name: article role: mapping visibility: shared owner_workflow_id: null entries: 2 ttl_seconds: null updated_at: '2026-08-26T10:00:00Z' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'State Store' '/api/v1/state/namespaces/{id}/entries': get: summary: 'List the entries of a namespace' operationId: listTheEntriesOfANamespace description: 'Paginated, narrowable by key with `?q=`.' parameters: - in: query name: q description: 'Narrow to entries whose key contains this.' required: false schema: type: string description: 'Narrow to entries whose key contains this.' examples: - 'pim:12345' - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 7 key: type: string examples: - 'pim:12345' value: type: string examples: - 'erp:A-8891' last_run_id: type: integer examples: - 42 updated_at: type: string examples: - '2026-08-26T10:00:00Z' examples: - - id: 7 key: 'pim:12345' value: 'erp:A-8891' last_run_id: 42 updated_at: '2026-08-26T10:00:00Z' meta: type: object properties: current_page: type: integer examples: - 1 per_page: type: integer examples: - 25 total: type: integer examples: - 1 last_page: type: integer examples: - 1 examples: - data: - id: 7 key: 'pim:12345' value: 'erp:A-8891' last_run_id: 42 updated_at: '2026-08-26T10:00:00Z' meta: current_page: 1 per_page: 25 total: 1 last_page: 1 429: $ref: '#/components/responses/TooManyRequests' tags: - 'State Store' requestBody: required: false content: application/json: schema: type: object properties: q: type: - string - 'null' description: 'Must not be greater than 255 characters.' examples: - b per_page: type: integer description: 'Must be at least 1. Must not be greater than 100.' examples: - 22 parameters: - in: path name: id description: 'The namespace id.' required: true schema: type: integer examples: - 1 '/api/v1/state/namespaces/{id}/entries/{entryId}': delete: summary: 'Delete one entry' operationId: deleteOneEntry description: "The correction path for a wrong correlation or a stuck cursor. Audited,\nbecause an operator editing what a run wrote is exactly the kind of\nchange somebody will want to trace back later." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'State Store' parameters: - in: path name: id description: 'The namespace id.' required: true schema: type: integer examples: - 1 - in: path name: entryId description: 'The entry id.' required: true schema: type: integer examples: - 7 '/api/v1/state/namespaces/{id}': delete: summary: 'Delete a namespace' operationId: deleteANamespace description: "Removes the namespace and every entry in it. The explicit act a shared\nnamespace requires: it outlives the workflows that declared it, so\nnothing else ever removes one." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'State Store' parameters: - in: path name: id description: 'The namespace id.' required: true schema: type: integer examples: - 1 /api/v1/templates: get: summary: 'List templates' operationId: listTemplates description: "Returns the latest registered version of each template, without the\n(heavy) embedded workflow `definition` blob — that ships only on `show`." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: slug: type: string examples: - 'revenexx:bc-sales-order-digest' version: type: string examples: - 1.0.0 category: type: string examples: - sales level: type: string examples: - beginner name: type: string examples: - 'Daily Sales-Order Digest' shortDescription: type: string examples: - 'Email a daily summary of Business Central sales orders.' description: type: - string - 'null' examples: - null icon: type: string examples: - 'mdi:email-newsletter' industries: type: array items: type: string examples: - - any vendors: type: array items: type: string examples: - - microsoft - business-central images: type: array items: type: object properties: src: type: string examples: - 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: type: object properties: en: type: string examples: - 'Business Central banner' title: type: object properties: en: type: string examples: - 'Business Central' category: type: string examples: - banner examples: - - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'Business Central banner' title: en: 'Business Central' category: banner triggerTypes: type: array items: type: string examples: - - schedule examples: - - slug: 'revenexx:bc-sales-order-digest' version: 1.0.0 category: sales level: beginner name: 'Daily Sales-Order Digest' shortDescription: 'Email a daily summary of Business Central sales orders.' description: null icon: 'mdi:email-newsletter' industries: - any vendors: - microsoft - business-central images: - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'Business Central banner' title: en: 'Business Central' category: banner triggerTypes: - schedule examples: - data: - slug: 'revenexx:bc-sales-order-digest' version: 1.0.0 category: sales level: beginner name: 'Daily Sales-Order Digest' shortDescription: 'Email a daily summary of Business Central sales orders.' description: null icon: 'mdi:email-newsletter' industries: - any vendors: - microsoft - business-central images: - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'Business Central banner' title: en: 'Business Central' category: banner triggerTypes: - schedule 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 429: $ref: '#/components/responses/TooManyRequests' tags: - Templates '/api/v1/templates/{slug}': get: summary: 'Show a template' operationId: showATemplate description: "Returns the latest registered version of the template identified by its\nnamespaced slug, including the embedded workflow `definition` used to\ninstantiate a new workflow." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: object properties: slug: type: string examples: - 'revenexx:bc-sales-order-digest' version: type: string examples: - 1.0.0 category: type: string examples: - sales level: type: string examples: - beginner name: type: string examples: - 'Daily Sales-Order Digest' shortDescription: type: string examples: - 'Email a daily summary of Business Central sales orders.' description: type: - string - 'null' examples: - null icon: type: string examples: - 'mdi:email-newsletter' industries: type: array items: type: string examples: - - any vendors: type: array items: type: string examples: - - microsoft - business-central images: type: array items: type: object properties: src: type: string examples: - 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: type: object properties: en: type: string examples: - 'Business Central banner' title: type: object properties: en: type: string examples: - 'Business Central' category: type: string examples: - banner examples: - - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'Business Central banner' title: en: 'Business Central' category: banner triggerTypes: type: array items: type: string examples: - - schedule blob_version: type: string examples: - v0-draft definition: type: object properties: nodeManifestVersion: type: string examples: - v0-draft name: type: string examples: - 'Daily Sales-Order Digest' nodes: type: array examples: - [] edges: type: array examples: - [] triggers: type: array items: type: object properties: handle: type: string examples: - a1c0ffee-0001-4001-8001-000000000001 type: type: string examples: - schedule config: type: object properties: cron: type: string examples: - '0 7 * * *' examples: - - handle: a1c0ffee-0001-4001-8001-000000000001 type: schedule config: cron: '0 7 * * *' examples: - data: slug: 'revenexx:bc-sales-order-digest' version: 1.0.0 category: sales level: beginner name: 'Daily Sales-Order Digest' shortDescription: 'Email a daily summary of Business Central sales orders.' description: null icon: 'mdi:email-newsletter' industries: - any vendors: - microsoft - business-central images: - src: 'https://api.example.com/v1/node-packages/images/2b1c0000-0000-4000-8000-000000000000?expires=1750000000&signature=abcdef' alt: en: 'Business Central banner' title: en: 'Business Central' category: banner triggerTypes: - schedule blob_version: v0-draft definition: nodeManifestVersion: v0-draft name: 'Daily Sales-Order Digest' nodes: [] edges: [] triggers: - handle: a1c0ffee-0001-4001-8001-000000000001 type: schedule config: cron: '0 7 * * *' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Templates parameters: - in: path name: slug description: 'Namespaced template slug.' required: true schema: type: string examples: - 'revenexx:slack-to-crm' '/api/v1/templates/{slug}/requirements': get: summary: 'Template installation requirements' operationId: templateInstallationRequirements description: "Lists the credential types and secret keys the template's workflow\nreferences, cross-referenced against the current tenant (how many\ncredential instances already exist, whether each secret key is stored) so\nan install UI can have the user provision what is missing first." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: object properties: credentialTypes: type: array items: type: object properties: slug: type: string examples: - 'revenexx:business-central' registered: type: boolean examples: - true existingInstances: type: integer examples: - 1 usedBy: type: array items: type: object properties: nodeId: type: string examples: - list_orders configKey: type: string examples: - credentials examples: - - nodeId: list_orders configKey: credentials version: type: string examples: - 1.0.0 name: type: string examples: - 'Business Central' authKind: type: string examples: - oauth2-client-credentials fields: type: array examples: - [] examples: - - slug: 'revenexx:business-central' registered: true existingInstances: 1 usedBy: - nodeId: list_orders configKey: credentials version: 1.0.0 name: 'Business Central' authKind: oauth2-client-credentials fields: [] secrets: type: array examples: - [] examples: - data: credentialTypes: - slug: 'revenexx:business-central' registered: true existingInstances: 1 usedBy: - nodeId: list_orders configKey: credentials version: 1.0.0 name: 'Business Central' authKind: oauth2-client-credentials fields: [] secrets: [] 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Templates parameters: - in: path name: slug description: 'Namespaced template slug.' required: true schema: type: string examples: - 'revenexx:slack-to-crm' '/api/v1/templates/{slug}/instantiate': post: summary: 'Instantiate a template' operationId: instantiateATemplate description: "Creates a new workflow (and its triggers) for the current tenant from the\ntemplate's blueprint. Trigger handles are regenerated so repeated installs\nnever collide; the workflow and all triggers start inactive. Returns the\ncreated workflow." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 201: description: Created content: application/json: schema: type: object properties: id: type: integer examples: - 1 name: type: string examples: - 'Daily Sales-Order Digest' description: type: - string - 'null' examples: - null blob_definition_version: type: string examples: - v0-draft blob: type: object properties: { } active: type: boolean examples: - false revision: type: integer examples: - 1 created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - id: 1 name: 'Daily Sales-Order Digest' description: null blob_definition_version: v0-draft blob: { } active: false revision: 1 created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The given data was invalid.' errors: type: object properties: credential_bindings: type: array items: type: string examples: - - 'The credential_bindings field must be an array.' examples: - message: 'The given data was invalid.' errors: credential_bindings: - 'The credential_bindings field must be an array.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Templates requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Optional workflow name override; defaults to the template name. Must not be greater than 255 characters.' examples: - 'My Slack flow' credential_bindings: type: array description: 'Must be a valid UUID.' items: type: array description: '' items: type: string examples: - - 6ff8f7f6-1eb3-3525-be4a-3932c805afed examples: - - - 6ff8f7f6-1eb3-3525-be4a-3932c805afed parameters: - in: path name: slug description: 'Namespaced template slug.' required: true schema: type: string examples: - 'revenexx:slack-to-crm' /api/v1/secrets: get: summary: 'List secrets' operationId: listSecrets description: "Returns the alphabetically sorted secrets of the current tenant in `data` —\nkey plus timestamps, never the value. `updated_at` is when the value was\nlast written, which is what \"when was this last rotated\" asks for;\noverwriting a secret with the same plaintext still counts as a write.\n\nReplaces the earlier `{\"keys\": [...]}` envelope. Every consumer of this\nendpoint is first-party and reads `data`." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: key: type: string examples: - SLACK_WEBHOOK_URL created_at: type: string examples: - '2026-05-04T08:50:00.000000Z' updated_at: type: string examples: - '2026-05-04T08:50:00.000000Z' examples: - - key: SLACK_WEBHOOK_URL created_at: '2026-05-04T08:50:00.000000Z' updated_at: '2026-05-04T08:50:00.000000Z' - key: STRIPE_API_KEY created_at: '2026-05-05T08:50:00.000000Z' updated_at: '2026-07-01T11:02:00.000000Z' examples: - data: - key: SLACK_WEBHOOK_URL created_at: '2026-05-04T08:50:00.000000Z' updated_at: '2026-05-04T08:50:00.000000Z' - key: STRIPE_API_KEY created_at: '2026-05-05T08:50:00.000000Z' updated_at: '2026-07-01T11:02:00.000000Z' 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Tenant Secrets' post: summary: 'Create or update a secret' operationId: createOrUpdateASecret description: "Upserts a secret value for the given key within the current tenant.\nReturns 201 on creation, 200 on update. The value is never echoed back." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Updated content: application/json: schema: type: object properties: key: type: string examples: - STRIPE_API_KEY created_at: type: string examples: - '2026-05-04T08:50:00Z' updated_at: type: string examples: - '2026-05-05T08:50:00Z' examples: - key: STRIPE_API_KEY created_at: '2026-05-04T08:50:00Z' updated_at: '2026-05-05T08:50:00Z' 201: description: Created content: application/json: schema: type: object properties: key: type: string examples: - STRIPE_API_KEY created_at: type: string examples: - '2026-05-05T08:50:00Z' updated_at: type: string examples: - '2026-05-05T08:50:00Z' examples: - key: STRIPE_API_KEY created_at: '2026-05-05T08:50:00Z' updated_at: '2026-05-05T08:50:00Z' 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The key field format is invalid.' errors: type: object properties: key: type: array items: type: string examples: - - ... examples: - message: 'The key field format is invalid.' errors: key: - ... 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Tenant Secrets' requestBody: required: true content: application/json: schema: type: object properties: key: type: string description: 'Env-style key, must match `^[A-Z0-9_]+$`.' examples: - STRIPE_API_KEY value: type: string description: 'The secret value.' examples: - sk_live_abc123 required: - key - value '/api/v1/secrets/{key}': patch: summary: 'Update a secret value' operationId: updateASecretValue description: "Replaces the value of an existing secret. The key is immutable — create\na new secret and delete the old one if you need to rename a key. Returns\n404 if the key does not exist for the current tenant." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Updated content: application/json: schema: type: object properties: key: type: string examples: - bc-client-secret created_at: type: string examples: - '2026-05-04T08:50:00Z' updated_at: type: string examples: - '2026-06-12T10:00:00Z' examples: - key: bc-client-secret created_at: '2026-05-04T08:50:00Z' updated_at: '2026-06-12T10:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The value field is required.' errors: type: object properties: value: type: array items: type: string examples: - - ... examples: - message: 'The value field is required.' errors: value: - ... 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Tenant Secrets' requestBody: required: true content: application/json: schema: type: object properties: value: type: string description: 'The new secret value.' examples: - sk_live_new_value required: - value delete: summary: 'Delete a secret' operationId: deleteASecret description: "Removes the secret identified by `{key}` for the current tenant. The\nlookup is explicitly scoped to the current tenant, so a key belonging\nto another tenant — or one that does not exist at all — yields a 404\nwithout leaking existence either way." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Tenant Secrets' parameters: - in: path name: key description: 'The secret key to update.' required: true schema: type: string examples: - bc-client-secret '/api/v1/workflows/{workflowId}/triggers': get: summary: 'List triggers for a workflow' operationId: listTriggersForAWorkflow description: '' parameters: - in: query name: next_fire_count description: 'optional How many upcoming firings each schedule reports (1–10). Defaults to 1.' required: false schema: type: integer description: 'optional How many upcoming firings each schedule reports (1–10). Defaults to 1.' examples: - 3 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: '' content: application/json: schema: oneOf: - description: Success type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 12 workflow_id: type: integer examples: - 1 handle: type: string examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: type: string examples: - webhook name: type: string examples: - 'PSP webhook' config: type: object properties: method: type: string examples: - POST public: type: boolean examples: - true inbound: type: object properties: endpoint_id: type: string examples: [inb_abc123] url: type: string examples: ['https://webhooks.revenexx.com/in/inb_abc123'] active: type: boolean examples: - true created_at: type: string examples: - '2026-06-15T10:00:00Z' updated_at: type: string examples: - '2026-06-15T10:00:00Z' examples: - - id: 12 workflow_id: 1 handle: 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: webhook name: 'PSP webhook' config: method: POST public: true inbound: endpoint_id: inb_abc123 url: 'https://webhooks.revenexx.com/in/inb_abc123' active: true created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' examples: - data: - id: 12 workflow_id: 1 handle: 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: webhook name: 'PSP webhook' config: method: POST public: true inbound: endpoint_id: inb_abc123 url: 'https://webhooks.revenexx.com/in/inb_abc123' active: true created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' - description: 'A schedule trigger' type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 13 workflow_id: type: integer examples: - 1 handle: type: string examples: - 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c type: type: string examples: - schedule name: type: - string - 'null' examples: - null config: type: object properties: cron: type: string examples: - '0 3 * * *' timezone: type: string examples: - Europe/Berlin active: type: boolean examples: - true schedule: type: - object - 'null' properties: cron: type: string examples: - '0 3 * * *' timezone: type: string examples: - Europe/Berlin timezone_source: type: - string - 'null' description: "`schedule` when the zone is the schedule's own, `platform_default` when it carries none and runs in the platform's." examples: - schedule holds_firings: type: boolean description: 'Whether the platform will fire this schedule. False whenever the scheduler would never select it — see `not_firing_reason`.' examples: - true not_firing_reason: type: - string - 'null' description: 'Why there is no next firing: trigger_paused, workflow_inactive, workflow_not_ready, no_cron, invalid_cron, unknown_timezone, unsatisfiable. Null when it does fire.' examples: - null next_fire_at: type: string examples: - '2026-08-22T01:00:00Z' next_fire_times: type: array description: "The upcoming firings, each as a UTC instant (`at`) and the same moment in the schedule's own zone (`local`) — whose offset says which side of a clock change it falls on. Empty when the schedule holds none." items: type: object properties: at: { type: string, examples: ['2026-08-22T01:00:00Z'] } local: { type: string, examples: ['2026-08-22T03:00:00+02:00'] } examples: - - { at: '2026-08-22T01:00:00Z', local: '2026-08-22T03:00:00+02:00' } last_dispatch_at: type: - string - 'null' description: 'When the scheduler last DECIDED to fire this trigger. Stamped before the run exists, so it can differ from `last_run` — a firing that was dispatched and then lost.' examples: - '2026-08-21T01:00:00Z' last_run: type: - object - 'null' properties: id: type: integer examples: [4711] status: type: string examples: [completed] error: type: [string, 'null'] examples: [null] started_at: type: string examples: ['2026-08-21T01:00:01Z'] completed_at: type: string examples: ['2026-08-21T01:00:44Z'] description: 'The last run this schedule actually started; null when it has never run.' required: - holds_firings - next_fire_times description: 'Present only on `schedule` triggers: the firings the platform actually holds, and the last run it really performed.' created_at: type: string examples: - '2026-06-15T10:00:00Z' updated_at: type: string examples: - '2026-06-15T10:00:00Z' examples: - - id: 13 workflow_id: 1 handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c type: schedule name: null config: cron: '0 3 * * *' timezone: Europe/Berlin active: true schedule: cron: '0 3 * * *' timezone: Europe/Berlin timezone_source: schedule holds_firings: true not_firing_reason: null next_fire_at: '2026-08-22T01:00:00Z' next_fire_times: - at: '2026-08-22T01:00:00Z' local: '2026-08-22T03:00:00+02:00' last_dispatch_at: '2026-08-21T01:00:00Z' last_run: id: 4711 status: completed error: null started_at: '2026-08-21T01:00:01Z' completed_at: '2026-08-21T01:00:44Z' created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' examples: - data: - id: 13 workflow_id: 1 handle: 9f1c8e2a-0b4d-4f7e-9a3c-1d2e3f4a5b6c type: schedule name: null config: cron: '0 3 * * *' timezone: Europe/Berlin active: true schedule: cron: '0 3 * * *' timezone: Europe/Berlin timezone_source: schedule holds_firings: true not_firing_reason: null next_fire_at: '2026-08-22T01:00:00Z' next_fire_times: - at: '2026-08-22T01:00:00Z' local: '2026-08-22T03:00:00+02:00' last_dispatch_at: '2026-08-21T01:00:00Z' last_run: id: 4711 status: completed error: null started_at: '2026-08-21T01:00:01Z' completed_at: '2026-08-21T01:00:44Z' created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' 429: $ref: '#/components/responses/TooManyRequests' tags: - Triggers post: summary: 'Create a trigger' operationId: createATrigger description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 201: description: 'Public webhook trigger' content: application/json: schema: type: object properties: id: type: integer examples: - 12 workflow_id: type: integer examples: - 1 handle: type: string examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: type: string examples: - webhook name: type: string examples: - 'PSP webhook' config: type: object properties: method: type: string examples: - POST public: type: boolean examples: - true inbound: type: object properties: endpoint_id: type: string examples: - inb_abc123 url: type: string examples: - 'https://webhooks.revenexx.com/in/inb_abc123' description: 'Server-owned ingress registration (`endpoint_id`, `url`) for a public webhook trigger. Clients cannot set or modify it.' active: type: boolean examples: - true created_at: type: string examples: - '2026-06-15T10:00:00Z' updated_at: type: string examples: - '2026-06-15T10:00:00Z' inbound_secret: type: string description: 'Present ONLY right after a public webhook endpoint is provisioned — the ingress ingest token, returned once and never stored (ADR-0066).' examples: - whsec_ing_onetime examples: - id: 12 workflow_id: 1 handle: 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: webhook name: 'PSP webhook' config: method: POST public: true inbound: endpoint_id: inb_abc123 url: 'https://webhooks.revenexx.com/in/inb_abc123' active: true created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:00:00Z' inbound_secret: whsec_ing_onetime 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The config.method field is required.' errors: type: object properties: config.method: type: array items: type: string examples: - - 'The config.method field is required.' examples: - message: 'The config.method field is required.' errors: config.method: - 'The config.method field is required.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Triggers requestBody: required: true content: application/json: schema: type: object properties: handle: type: string description: 'Stable UUID referenced by the workflow blob edges. Must be unique per tenant. Must be a valid UUID.' examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: type: string description: 'Trigger entry-point type.' enum: - manual - schedule - webhook - event examples: - webhook name: type: - string - 'null' description: 'Optional human-readable label.' examples: - 'PSP webhook' active: type: boolean description: 'Whether the trigger is enabled. Defaults to true.' examples: - true config: type: - object - 'null' description: 'Per-type trigger configuration. Fields depend on `type`: webhook → `method` (required HTTP method), optional `public` (expose via the inbound ingress, ADR-0066 — provisioning returns `inbound_secret` once), `authProfile` (ingress verification profile: none|token|basic|hmac-generic|stripe|standard-webhooks; default token) and `authConfig`, whose keys belong to the chosen profile (token: header, prefix; basic: username; hmac-generic: header, prefix, algorithm sha256|sha512, encoding hex|base64; none, stripe, standard-webhooks: none; mode enforce|annotate for all of them) — a key another profile owns is rejected. `secretRef` is deprecated — superseded by authProfile/authConfig; still accepted, and still read when a workflow template declares its required secrets, but it verifies nothing on the inbound path. schedule → `cron` (required) and optional `timezone` (IANA); manual → none.' properties: { } examples: - method: POST public: true authProfile: token required: - handle - type parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/triggers/{triggerId}': put: summary: 'Update a trigger' operationId: updateATrigger description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Public webhook trigger' content: application/json: schema: type: object properties: id: type: integer examples: - 12 workflow_id: type: integer examples: - 1 handle: type: string examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: type: string examples: - webhook name: type: string examples: - 'PSP webhook' config: type: object properties: method: type: string examples: - POST public: type: boolean examples: - true inbound: type: object properties: endpoint_id: type: string examples: - inb_abc123 url: type: string examples: - 'https://webhooks.revenexx.com/in/inb_abc123' description: 'Server-owned ingress registration (`endpoint_id`, `url`) for a public webhook trigger. Clients cannot set or modify it.' active: type: boolean examples: - true created_at: type: string examples: - '2026-06-15T10:00:00Z' updated_at: type: string examples: - '2026-06-15T10:05:00Z' inbound_secret: type: string description: 'Present ONLY right after a public webhook endpoint is provisioned — the ingress ingest token, returned once and never stored (ADR-0066).' examples: - whsec_ing_onetime examples: - id: 12 workflow_id: 1 handle: 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: webhook name: 'PSP webhook' config: method: POST public: true inbound: endpoint_id: inb_abc123 url: 'https://webhooks.revenexx.com/in/inb_abc123' active: true created_at: '2026-06-15T10:00:00Z' updated_at: '2026-06-15T10:05:00Z' inbound_secret: whsec_ing_onetime 422: description: 'Validation failed' content: application/json: schema: type: object properties: message: type: string examples: - 'The config.method field is required.' errors: type: object properties: config.method: type: array items: type: string examples: - - 'The config.method field is required.' examples: - message: 'The config.method field is required.' errors: config.method: - 'The config.method field is required.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Triggers requestBody: required: true content: application/json: schema: type: object properties: type: type: string description: 'Trigger entry-point type.' enum: - manual - schedule - webhook - event examples: - webhook name: type: - string - 'null' description: 'Optional human-readable label.' examples: - 'PSP webhook' active: type: boolean description: 'Whether the trigger is enabled.' examples: - true config: type: - object - 'null' description: 'Per-type trigger configuration. Fields depend on `type`: webhook → `method` (required HTTP method), optional `public` (expose via the inbound ingress, ADR-0066 — provisioning returns `inbound_secret` once), `authProfile` (ingress verification profile: none|token|basic|hmac-generic|stripe|standard-webhooks; default token) and `authConfig`, whose keys belong to the chosen profile (token: header, prefix; basic: username; hmac-generic: header, prefix, algorithm sha256|sha512, encoding hex|base64; none, stripe, standard-webhooks: none; mode enforce|annotate for all of them) — a key another profile owns is rejected. `secretRef` is deprecated — superseded by authProfile/authConfig; still accepted, and still read when a workflow template declares its required secrets, but it verifies nothing on the inbound path. schedule → `cron` (required) and optional `timezone` (IANA); manual → none.' properties: { } examples: - method: POST public: true authProfile: token required: - type - active delete: summary: 'Delete a trigger' operationId: deleteATrigger description: '' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 429: $ref: '#/components/responses/TooManyRequests' tags: - Triggers parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: triggerId description: 'The trigger id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/triggers/{triggerHandle}/rotate-secret': post: summary: 'Rotate the ingest secret of a public webhook trigger' operationId: rotateTheIngestSecretOfAPublicWebhookTrigger description: "Generates a fresh ingest secret on the trigger's ingress endpoint. The\nprevious secret stops working immediately. Only available for webhook\ntriggers that are exposed via the inbound ingress (`config.public`)." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Rotated content: application/json: schema: type: object properties: inbound_secret: type: string description: 'The freshly generated ingress ingest token — returned once and never stored (ADR-0066).' examples: - whsec_ing_onetime examples: - inbound_secret: whsec_ing_onetime 409: description: '' content: application/json: schema: oneOf: - description: 'Not exposed' type: object properties: message: type: string examples: - 'Trigger is not exposed via the inbound ingress; set config.public first.' examples: - message: 'Trigger is not exposed via the inbound ingress; set config.public first.' - description: 'Ingress unavailable' type: object properties: message: type: string examples: - 'Secret rotation is not available: the inbound ingress is not configured.' examples: - message: 'Secret rotation is not available: the inbound ingress is not configured.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Triggers parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: triggerHandle description: "The trigger's UUID handle." required: true schema: type: string examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed '/api/v1/webhooks/{handle}': get: summary: 'Fire a webhook trigger' operationId: fireAWebhookTrigger description: "Resolves the webhook trigger by handle, enforces the configured HTTP\nmethod, and starts a run. The mode follows the workflow's `execution_mode`\n(`async_only` → 202; `sync_only`/`caller_decides` → wait and return the\nresult), and can be overridden per request with `?mode=sync|async` when\nthe policy allows it — a conflicting `mode` is rejected with 422." parameters: - in: query name: mode description: 'optional Force `sync` or `async`. Honored for `caller_decides` workflows; conflicting with `async_only`/`sync_only` yields 422.' required: false schema: type: string description: 'optional Force `sync` or `async`. Honored for `caller_decides` workflows; conflicting with `async_only`/`sync_only` yields 422.' examples: - async - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Completed (sync)' content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - completed result: type: object properties: { } examples: - id: 1 workflow_id: 1 status: completed result: { } 202: description: 'Accepted (async)' content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - running examples: - id: 1 workflow_id: 1 status: running 404: description: 'No such webhook trigger' content: application/json: schema: type: object properties: message: type: string examples: - 'No active webhook trigger with handle … exists.' examples: - message: 'No active webhook trigger with handle … exists.' 405: description: 'Method not allowed' content: application/json: schema: type: object properties: message: type: string examples: - 'This webhook expects POST.' examples: - message: 'This webhook expects POST.' 422: description: 'Mode conflicts with policy' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow does not allow sync execution (execution_mode=async_only).' examples: - message: 'Workflow does not allow sync execution (execution_mode=async_only).' 429: $ref: '#/components/responses/TooManyRequests' tags: - Webhooks parameters: - in: path name: handle description: 'The webhook trigger handle (UUID).' required: true schema: type: string examples: - 0190a9c0-1111-7000-8000-000000000000 /api/v1/workflows/used-credentials: get: summary: "List credential ids used across the tenant's workflows" operationId: listCredentialIdsUsedAcrossTheTenantsWorkflows description: "Returns the distinct credential *instance* UUIDs referenced by every\nworkflow of the current tenant. Optionally filtered to a single\ncredential type slug via `?type={slug}` and/or to active workflows only\nvia `?active=true`." parameters: - in: query name: type description: 'Filter to a single credential type slug.' required: false schema: type: string description: 'Filter to a single credential type slug.' examples: - 'revenexx:smtp' - in: query name: active description: 'Only consider active workflows.' required: false schema: type: boolean description: 'Only consider active workflows.' examples: - true - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Used credentials' content: application/json: schema: type: object properties: data: type: array items: type: string examples: - - 9f3e2c1a-0000-4000-8000-000000000001 - 1ab2c3d4-0000-4000-8000-000000000002 examples: - data: - 9f3e2c1a-0000-4000-8000-000000000001 - 1ab2c3d4-0000-4000-8000-000000000002 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Credentials' /api/v1/workflows/used-credential-types: get: summary: "List credential type slugs used across the tenant's workflows" operationId: listCredentialTypeSlugsUsedAcrossTheTenantsWorkflows description: "Returns the distinct credential *type* slugs (e.g. `revenexx:smtp`)\nreferenced by every workflow of the current tenant. Optionally filtered\nto active workflows only via `?active=true`." parameters: - in: query name: active description: 'Only consider active workflows.' required: false schema: type: boolean description: 'Only consider active workflows.' examples: - true - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Used credential types' content: application/json: schema: type: object properties: data: type: array items: type: string examples: - - 'revenexx:smtp' - 'revenexx:deepl' examples: - data: - 'revenexx:smtp' - 'revenexx:deepl' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Credentials' '/api/v1/workflows/{workflowId}/credentials': get: summary: 'List credential ids used by a workflow' operationId: listCredentialIdsUsedByAWorkflow description: "Returns the distinct credential *instance* UUIDs the workflow's nodes\nreference, optionally filtered to a single credential type slug via\n`?type={slug}`." parameters: - in: query name: type description: 'Filter to a single credential type slug.' required: false schema: type: string description: 'Filter to a single credential type slug.' examples: - 'revenexx:smtp' - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Used credentials' content: application/json: schema: type: object properties: data: type: array items: type: string examples: - - 9f3e2c1a-0000-4000-8000-000000000001 - 1ab2c3d4-0000-4000-8000-000000000002 examples: - data: - 9f3e2c1a-0000-4000-8000-000000000001 - 1ab2c3d4-0000-4000-8000-000000000002 404: description: 'Workflow not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Credentials' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/credential-types': get: summary: 'List credential type slugs used by a workflow' operationId: listCredentialTypeSlugsUsedByAWorkflow description: "Returns the distinct credential *type* slugs (e.g. `revenexx:smtp`) the\nworkflow's nodes reference through their `credentials-ref` config fields." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Used credential types' content: application/json: schema: type: object properties: data: type: array items: type: string examples: - - 'revenexx:smtp' - 'revenexx:deepl' examples: - data: - 'revenexx:smtp' - 'revenexx:deepl' 404: description: 'Workflow not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Credentials' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/revisions': get: summary: 'List workflow revisions' operationId: listWorkflowRevisions description: "Returns the archived revisions of a workflow, newest first, paginated.\nList items carry metadata only — use the show endpoint to fetch a\nsingle revision's full `blob` and `triggers`." parameters: - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Number of revisions per page (1–100). Defaults to 25. Must be at least 1. Must not be greater than 100.' required: false schema: type: - integer - 'null' description: 'Number of revisions per page (1–100). Defaults to 25. Must be at least 1. Must not be greater than 100.' examples: - 25 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 7 revision: type: integer examples: - 2 name: type: string examples: - 'Sync orders' description: type: - string - 'null' examples: - null active: type: boolean examples: - true blob_definition_version: type: string examples: - v0-draft created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T11:00:00Z' archived_at: type: string examples: - '2026-05-06T12:00:00Z' examples: - - id: 7 revision: 2 name: 'Sync orders' description: null active: true blob_definition_version: v0-draft created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T11:00:00Z' archived_at: '2026-05-06T12:00:00Z' meta: type: object properties: current_page: type: integer examples: - 1 per_page: type: integer examples: - 25 total: type: integer examples: - 1 last_page: type: integer examples: - 1 examples: - data: - id: 7 revision: 2 name: 'Sync orders' description: null active: true blob_definition_version: v0-draft created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T11:00:00Z' archived_at: '2026-05-06T12:00:00Z' meta: current_page: 1 per_page: 25 total: 1 last_page: 1 404: description: 'Workflow not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Revisions' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/revisions/{revision}': get: summary: 'Show a workflow revision' operationId: showAWorkflowRevision description: "Returns a single archived revision in full, including its `blob` and\n`triggers` snapshot." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: id: type: integer examples: - 7 revision: type: integer examples: - 1 name: type: string examples: - 'Sync orders' description: type: - string - 'null' examples: - null active: type: boolean examples: - true blob_definition_version: type: string examples: - v0-draft blob: type: object properties: { } triggers: type: array examples: - [] created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T11:00:00Z' archived_at: type: string examples: - '2026-05-06T12:00:00Z' examples: - id: 7 revision: 1 name: 'Sync orders' description: null active: true blob_definition_version: v0-draft blob: { } triggers: [] created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T11:00:00Z' archived_at: '2026-05-06T12:00:00Z' 404: description: 'Revision or workflow not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Revisions' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: revision description: 'The archived revision number.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/revisions/{revision}/restore': post: summary: 'Restore a workflow revision' operationId: restoreAWorkflowRevision description: "Re-applies the given archived revision's definition (`blob`,\n`blob_definition_version`, and `triggers`) to the workflow as a brand-new\nrevision. The current state is first snapshotted into the history, the\n`revision` counter is bumped, and a fresh bundle build is dispatched. The\nworkflow's `name`, `description` and `execution_mode` are left unchanged.\n`active` is preserved too, except that restoring a schema- or\nmanifest-invalid revision onto an active workflow forces it inactive to\nkeep the `active ⇒ valid` invariant (PO-186) — the restore still succeeds.\n\nRestoring a revision whose definition is identical to the current one is\na safe no-op for the `revision` counter (no new history row is written),\nthough triggers are still re-synced from the snapshot." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Restored content: application/json: schema: type: object properties: id: type: integer examples: - 1 name: type: string examples: - 'Sync orders' revision: type: integer examples: - 3 build_status: type: string examples: - pending blob: type: object properties: { } triggers: type: array examples: - [] examples: - id: 1 name: 'Sync orders' revision: 3 build_status: pending blob: { } triggers: [] 404: description: 'Revision or workflow not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Revisions' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: revision description: 'The archived revision number to restore.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs': get: summary: 'List runs for a workflow' operationId: listRunsForAWorkflow description: 'Returns all runs for the workflow, newest first.' parameters: - in: query name: page description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' required: false schema: type: - integer - 'null' description: 'Page number to retrieve (1-based). Defaults to 1. Must be at least 1.' examples: - 1 - in: query name: per_page description: 'Number of runs per page (1–100). Defaults to 25. Must be at least 1. Must not be greater than 100.' required: false schema: type: - integer - 'null' description: 'Number of runs per page (1–100). Defaults to 25. Must be at least 1. Must not be greater than 100.' examples: - 25 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array examples: - [] meta: type: object properties: current_page: type: integer examples: - 1 per_page: type: integer examples: - 25 total: type: integer examples: - 0 last_page: type: integer examples: - 1 examples: - data: [] meta: current_page: 1 per_page: 25 total: 0 last_page: 1 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' post: summary: 'Start a workflow run' operationId: startAWorkflowRun description: "Execution behavior depends on workflow `execution_mode` and optional\nrequest `mode`:\n\n- Async: starts the run and returns HTTP 202 with status `running`.\n- Sync: waits for worker completion and returns HTTP 200/422/504/503\n with the final outcome.\n\nFor `caller_decides`, omitting `mode` defaults to async execution." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: 'Sync completed' content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - completed result: type: object properties: node-1: type: object properties: ok: type: boolean examples: - true completed_at: type: string examples: - '2026-05-22T10:00:30Z' examples: - id: 1 workflow_id: 1 status: completed result: node-1: ok: true completed_at: '2026-05-22T10:00:30Z' 202: description: Accepted content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - running temporal_workflow_id: type: string examples: - 'tenant:1:abc' started_at: type: string examples: - '2026-05-13T10:00:00Z' examples: - id: 1 workflow_id: 1 status: running temporal_workflow_id: 'tenant:1:abc' started_at: '2026-05-13T10:00:00Z' 404: description: 'Workflow not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 422: description: '' content: application/json: schema: oneOf: - description: 'Unresolved manifest issues (PO-186)' type: object properties: message: type: string examples: - 'Workflow configuration has unresolved issues and cannot run. Fix the reported fields and try again.' errors: type: object properties: blob.nodes.0.config.credentials: type: array items: type: string examples: - - "Required config field 'credentials' is missing." examples: - message: 'Workflow configuration has unresolved issues and cannot run. Fix the reported fields and try again.' errors: blob.nodes.0.config.credentials: - "Required config field 'credentials' is missing." - description: 'Mode not allowed' type: object properties: message: type: string examples: - 'Workflow does not allow sync execution (execution_mode=async_only).' examples: - message: 'Workflow does not allow sync execution (execution_mode=async_only).' - description: 'Sync failed' type: object properties: message: type: string examples: - 'Workflow execution failed.' error: type: string examples: - 'node execution failed' run: type: object properties: id: type: integer examples: - 1 status: type: string examples: - failed examples: - message: 'Workflow execution failed.' error: 'node execution failed' run: id: 1 status: failed 501: description: 'Sync not supported by starter' content: application/json: schema: type: object properties: message: type: string examples: - 'Synchronous workflow execution is not supported by the configured run starter driver.' examples: - message: 'Synchronous workflow execution is not supported by the configured run starter driver.' 503: description: 'Worker unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow worker is unavailable.' examples: - message: 'Workflow worker is unavailable.' 504: description: 'Sync timeout' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow execution timed out.' error: type: string examples: - 'Sync run for workflow 1 exceeded timeout of 30 seconds.' examples: - message: 'Workflow execution timed out.' error: 'Sync run for workflow 1 exceeded timeout of 30 seconds.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' requestBody: required: false content: application/json: schema: type: object properties: trigger_payload: type: object description: 'optional Arbitrary trigger payload forwarded to the workflow.' properties: { } examples: - source: api trigger_handle: type: string description: "UUID handle of the trigger to run as. Optional; defaults to the workflow's manual trigger. Must be a valid UUID." examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed mode: type: string description: 'optional Requested run mode. Allowed: `sync`, `async`. Resolution depends on workflow execution_mode policy.' examples: - sync parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}': get: summary: 'Get a workflow run' operationId: getAWorkflowRun description: "Returns the current state of a workflow run. If the run is still\nin progress, the worker is polled for the latest Temporal status." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Completed content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - completed result: type: object properties: { } completed_at: type: string examples: - '2026-05-13T10:01:00Z' examples: - id: 1 workflow_id: 1 status: completed result: { } completed_at: '2026-05-13T10:01:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/details': get: summary: 'Get execution details for a workflow run' operationId: getExecutionDetailsForAWorkflowRun description: "Returns the execution description (status, timing, history length,\nparent linkage) via {@see WorkflowExecutionInspector}. The inspector\ntransparently routes between a live Temporal call and the local\nsnapshot taken when the run reached a terminal status; the response\ncarries a `source` field of `\"temporal\"` or `\"snapshot\"` so clients\nknow which view they are seeing." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: '' content: application/json: schema: oneOf: - description: Live type: object properties: source: type: string examples: - temporal data: type: object properties: workflow_id: type: string examples: - 'tenant:1:abc' run_id: type: string examples: - 0190... status: type: string examples: - running examples: - source: temporal data: workflow_id: 'tenant:1:abc' run_id: 0190... status: running - description: Snapshot type: object properties: source: type: string examples: - snapshot data: type: object properties: workflow_id: type: string examples: - 'tenant:1:abc' run_id: type: string examples: - 0190... status: type: string examples: - completed examples: - source: snapshot data: workflow_id: 'tenant:1:abc' run_id: 0190... status: completed 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Run not handed off to Temporal yet' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow run has no Temporal execution yet.' examples: - message: 'Workflow run has no Temporal execution yet.' 410: description: 'History unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow execution history is no longer available.' examples: - message: 'Workflow execution history is no longer available.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/history': get: summary: 'Get execution history for a workflow run' operationId: getExecutionHistoryForAWorkflowRun description: "Returns one page of history events via {@see WorkflowExecutionInspector}.\nLike the details endpoint, the response is hybrid-routed and carries\na `source` field. The `next_page_token` is opaque (source-tagged with\n`tmp:` or `snp:`); pass it verbatim to the next call. Note that\nswitching sources mid-pagination resets to page 1 (the response's\n`source` will change), which the client should treat as a fresh\npagination." parameters: - in: query name: page_token description: 'Opaque continuation token returned by the previous page. Omit for the first page. Must not be greater than 8192 characters.' example: null required: false schema: type: - string - 'null' description: 'Opaque continuation token returned by the previous page. Omit for the first page. Must not be greater than 8192 characters.' examples: - null - in: query name: page_size description: 'Maximum number of history events per page (1–1000). Defaults to the server-configured value. Must be at least 1. Must not be greater than 1000.' required: false schema: type: - integer - 'null' description: 'Maximum number of history events per page (1–1000). Defaults to the server-configured value. Must be at least 1. Must not be greater than 1000.' examples: - 200 - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: '' content: application/json: schema: oneOf: - description: 'Live page' type: object properties: source: type: string examples: - temporal data: type: array items: type: object properties: event_id: type: integer examples: - 1 event_type: type: string examples: - workflow_execution_started examples: - - event_id: 1 event_type: workflow_execution_started next_page_token: type: - string - 'null' examples: - null examples: - source: temporal data: - event_id: 1 event_type: workflow_execution_started next_page_token: null - description: 'Snapshot page' type: object properties: source: type: string examples: - snapshot data: type: array items: type: object properties: event_id: type: integer examples: - 1 examples: - - event_id: 1 next_page_token: type: - string - 'null' examples: - null examples: - source: snapshot data: - event_id: 1 next_page_token: null 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Run not handed off to Temporal yet' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow run has no Temporal execution yet.' examples: - message: 'Workflow run has no Temporal execution yet.' 410: description: 'History unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow execution history is no longer available.' examples: - message: 'Workflow execution history is no longer available.' 422: description: 'Invalid page token' content: application/json: schema: type: object properties: message: type: string examples: - 'Invalid page_token.' examples: - message: 'Invalid page_token.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/steps': get: summary: 'Get the per-node steps of a workflow run' operationId: getThePerNodeStepsOfAWorkflowRun description: "Projects the run's flat Temporal history into an ordered list of node\nexecutions (one per scheduling, so a looped node yields one step per\niteration) for the step-through replay inspector: per node the status,\ntiming, duration, decoded input/config/output and any error. Like the\nhistory endpoint the response is hybrid-routed and carries a `source`\nfield (`snapshot` for terminal snapshotted runs, `temporal` for live\nreads). Node ids match the workflow blob's canvas node ids.\n\nA failed step's `error` carries the failure's `code` (as `type`) and a\nbounded `message`, and never a stack trace. Where the failure came from\na node's own code it also carries `frames`: the author's file relative\nto the package it belongs to, a 1-based line and column, and the\nfunction name where one was recorded, innermost first. Only frames\ninside a node package appear. `frames` is absent where the failure\nproduced none, or where the artifact the run executed has no source map\nstored — which is the case for every run recorded before this existed." parameters: - in: query name: include_payloads description: "Whether to decode and include each step's input/config/output payloads. Defaults to true." required: false schema: type: - string - 'null' description: "Whether to decode and include each step's input/config/output payloads. Defaults to true." enum: - 'true' - 'false' - '1' - '0' examples: - true - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: '' content: application/json: schema: oneOf: - description: 'Projected steps' type: object properties: source: type: string examples: - snapshot run: type: object properties: status: type: string examples: - completed started_at: type: string examples: - '2026-05-22T10:00:00Z' completed_at: type: string examples: - '2026-05-22T10:00:05Z' steps: type: array items: type: object properties: node_id: type: string examples: - node-1 node_slug: type: string examples: - 'revenexx:http-request' activity_type: type: string examples: - executeNode status: type: string examples: - completed attempt: type: integer examples: - 0 scheduled_event_id: type: integer examples: - 5 duration_ms: type: integer examples: - 812 input: type: object properties: { } output: type: object properties: { } branch: type: - string - 'null' examples: - null error: type: - string - 'null' examples: - null examples: - - node_id: node-1 node_slug: 'revenexx:http-request' activity_type: executeNode status: completed attempt: 0 scheduled_event_id: 5 duration_ms: 812 input: [] output: [] branch: null error: null examples: - source: snapshot run: status: completed started_at: '2026-05-22T10:00:00Z' completed_at: '2026-05-22T10:00:05Z' steps: - node_id: node-1 node_slug: 'revenexx:http-request' activity_type: executeNode status: completed attempt: 0 scheduled_event_id: 5 duration_ms: 812 input: { } output: { } branch: null error: null - description: 'A step that failed inside a node' type: object properties: source: type: string examples: - snapshot run: type: object properties: status: type: string examples: - failed steps: type: array items: type: object properties: node_id: type: string examples: - f-empty-text node_slug: type: string examples: - 'revenexx:deepl-translate' activity_type: type: string examples: - executeNode status: type: string examples: - failed attempt: type: integer examples: - 0 scheduled_event_id: type: integer examples: - 5 duration_ms: type: integer examples: - 41 input: type: object properties: { } output: type: - string - 'null' examples: - null branch: type: - string - 'null' examples: - null error: type: object properties: message: type: string examples: - 'No text found to translate' type: type: string examples: - MISSING_TEXT frames: type: array items: type: object properties: file: { type: string, examples: ['@revenexx/integrations-nodes-core/src/nodes/DeeplTranslateNode.ts'] } line: { type: integer, examples: [5] } column: { type: integer, examples: [11] } name: { type: string, examples: [requireText] } examples: - - { file: '@revenexx/integrations-nodes-core/src/nodes/DeeplTranslateNode.ts', line: 5, column: 11, name: requireText } examples: - - node_id: f-empty-text node_slug: 'revenexx:deepl-translate' activity_type: executeNode status: failed attempt: 0 scheduled_event_id: 5 duration_ms: 41 input: [] output: null branch: null error: message: 'No text found to translate' type: MISSING_TEXT frames: - file: '@revenexx/integrations-nodes-core/src/nodes/DeeplTranslateNode.ts' line: 5 column: 11 name: requireText examples: - source: snapshot run: status: failed steps: - node_id: f-empty-text node_slug: 'revenexx:deepl-translate' activity_type: executeNode status: failed attempt: 0 scheduled_event_id: 5 duration_ms: 41 input: { } output: null branch: null error: message: 'No text found to translate' type: MISSING_TEXT frames: - file: '@revenexx/integrations-nodes-core/src/nodes/DeeplTranslateNode.ts' line: 5 column: 11 name: requireText 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Run not handed off to Temporal yet' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow run has no Temporal execution yet.' examples: - message: 'Workflow run has no Temporal execution yet.' 410: description: 'History unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow execution history is no longer available.' examples: - message: 'Workflow execution history is no longer available.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/stack-trace': get: summary: 'Get the current call stack of a running workflow' operationId: getTheCurrentCallStackOfARunningWorkflow description: "Issues the built-in `__stack_trace` query against the workflow worker\nand returns the formatted stack trace as plain text. Only meaningful\nwhile the run is still executing; terminal runs return 409.\n\nThis is where the *workflow* is suspended, not where a step failed. For\nthe latter, a failed step's `error.frames` on the steps endpoint names\nthe line the node's author wrote." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Running content: application/json: schema: type: object properties: stack_trace: type: string examples: - 'coroutine ...' examples: - stack_trace: 'coroutine ...' 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: '' content: application/json: schema: oneOf: - description: 'Run not handed off to Temporal yet' type: object properties: message: type: string examples: - 'Workflow run has no Temporal execution yet.' examples: - message: 'Workflow run has no Temporal execution yet.' - description: 'Run is no longer running' type: object properties: message: type: string examples: - 'Workflow run is no longer running.' examples: - message: 'Workflow run is no longer running.' 410: description: 'Execution unknown to Temporal' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow execution history is no longer available.' examples: - message: 'Workflow execution history is no longer available.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/cancel': post: summary: 'Cancel a running workflow run' operationId: cancelARunningWorkflowRun description: "Requests graceful cancellation of a still-running run: Temporal delivers a\ncancellation signal to the workflow, which may run cleanup before closing.\nThe run stays `running` until the next status reconcile flips it to\n`cancelled`, so this returns HTTP 202." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 202: description: 'Cancellation requested' content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - running examples: - id: 1 workflow_id: 1 status: running 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Run already terminal' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow run is no longer running.' examples: - message: 'Workflow run is no longer running.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' requestBody: required: false content: application/json: schema: type: object properties: reason: type: - string - 'null' description: 'Optional human-readable reason recorded on the Temporal execution and in the audit trail. Must not be greater than 255 characters.' examples: - 'Superseded by a newer run' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/terminate': post: summary: 'Terminate a running workflow run' operationId: terminateARunningWorkflowRun description: "Forcefully terminates a still-running run with no cleanup and flips it to\n`terminated` immediately." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Terminated content: application/json: schema: type: object properties: id: type: integer examples: - 1 workflow_id: type: integer examples: - 1 status: type: string examples: - terminated examples: - id: 1 workflow_id: 1 status: terminated 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: 'Run already terminal' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow run is no longer running.' examples: - message: 'Workflow run is no longer running.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' requestBody: required: false content: application/json: schema: type: object properties: reason: type: - string - 'null' description: 'Optional human-readable reason recorded on the Temporal execution and in the audit trail. Must not be greater than 255 characters.' examples: - 'Superseded by a newer run' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The run id.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/retry': post: summary: 'Re-run a previous workflow run' operationId: reRunAPreviousWorkflowRun description: "Starts a fresh run from the inputs (trigger payload + trigger handle) of\nan existing run, linking the new run to its source via\n`retried_from_run_id`. The source run may be in any state; the re-run\nalways executes against the workflow's currently-built bundle and runs\nasynchronously." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 0: description: 'Workflow not runnable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow bundle is not ready (build_' examples: - message: 'Workflow bundle is not ready (build_' 202: description: Accepted content: application/json: schema: type: object properties: id: type: integer examples: - 2 workflow_id: type: integer examples: - 1 status: type: string examples: - running retried_from_run_id: type: integer examples: - 1 examples: - id: 2 workflow_id: 1 status: running retried_from_run_id: 1 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 422: description: '' content: application/json: schema: oneOf: - description: 'Unresolved manifest issues (PO-186)' type: object properties: message: type: string examples: - 'Workflow configuration has unresolved issues and cannot run. Fix the reported fields and try again.' errors: type: object properties: blob.nodes.0.config.credentials: type: array items: type: string examples: - - "Required config field 'credentials' is missing." examples: - message: 'Workflow configuration has unresolved issues and cannot run. Fix the reported fields and try again.' errors: blob.nodes.0.config.credentials: - "Required config field 'credentials' is missing." - description: 'Trigger no longer resolvable' type: object properties: message: type: string examples: - 'No active trigger with handle ... exists on this workflow.' examples: - message: 'No active trigger with handle ... exists on this workflow.' 503: description: 'Worker unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow worker is unavailable.' examples: - message: 'Workflow worker is unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The id of the run to re-run.' required: true schema: type: integer examples: - 1 '/api/v1/workflows/{workflowId}/runs/{runId}/resume': post: summary: 'Resume a failed workflow run from its failed step' operationId: resumeAFailedWorkflowRunFromItsFailedStep description: "Resets the underlying Temporal execution to the workflow-task boundary\nbefore the failed step and re-drives it: already-completed steps keep\ntheir results, the failed step and everything downstream re-execute. The\nresumed run is tracked as a new run linked via `resumed_from_run_id`." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 202: description: Accepted content: application/json: schema: type: object properties: id: type: integer examples: - 2 workflow_id: type: integer examples: - 1 status: type: string examples: - running resumed_from_run_id: type: integer examples: - 1 examples: - id: 2 workflow_id: 1 status: running resumed_from_run_id: 1 404: description: 'Run not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 409: description: '' content: application/json: schema: oneOf: - description: 'Run not resumable state' type: object properties: message: type: string examples: - 'Only a failed run can be resumed.' examples: - message: 'Only a failed run can be resumed.' - description: 'Already resumed' type: object properties: message: type: string examples: - 'Workflow run has already been resumed.' examples: - message: 'Workflow run has already been resumed.' 422: description: 'No reset point' content: application/json: schema: type: object properties: message: type: string examples: - 'Workflow run cannot be resumed: no suitable reset point in its history.' examples: - message: 'Workflow run cannot be resumed: no suitable reset point in its history.' 503: description: 'Temporal unavailable' content: application/json: schema: type: object properties: message: type: string examples: - 'Temporal is currently unavailable.' examples: - message: 'Temporal is currently unavailable.' 429: $ref: '#/components/responses/TooManyRequests' tags: - 'Workflow Runs' requestBody: required: false content: application/json: schema: type: object properties: reason: type: - string - 'null' description: 'Optional human-readable reason recorded on the Temporal execution and in the audit trail. Must not be greater than 255 characters.' examples: - 'Superseded by a newer run' parameters: - in: path name: workflowId description: 'The workflow id.' required: true schema: type: integer examples: - 1 - in: path name: runId description: 'The id of the failed run to resume.' required: true schema: type: integer examples: - 1 /api/v1/workflows: get: summary: 'List workflows' operationId: listWorkflows description: 'Returns the workflows owned by the current tenant, ordered by name.' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: integer examples: - 1 name: type: string examples: - 'Sync orders' description: type: - string - 'null' examples: - null blob_definition_version: type: string examples: - v0-draft blob: type: object properties: { } active: type: boolean examples: - true revision: type: integer examples: - 1 created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - - id: 1 name: 'Sync orders' description: null blob_definition_version: v0-draft blob: [] active: true revision: 1 created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' examples: - data: - id: 1 name: 'Sync orders' description: null blob_definition_version: v0-draft blob: { } active: true revision: 1 created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 401: description: 'Missing or invalid token' content: application/json: schema: type: object properties: message: type: string examples: - Unauthenticated. examples: - message: Unauthenticated. 429: $ref: '#/components/responses/TooManyRequests' tags: - Workflows post: summary: 'Create a workflow' operationId: createAWorkflow description: "Persists a new workflow under the current tenant. The `name` must be\nunique within the tenant. The `blob` is validated against the schema\nregistered for the supplied `blob_definition_version`." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 201: description: Created content: application/json: schema: type: object properties: id: type: integer examples: - 1 name: type: string examples: - 'Sync orders' description: type: - string - 'null' examples: - null blob_definition_version: type: string examples: - v0-draft blob: type: object properties: { } active: type: boolean examples: - true revision: type: integer examples: - 1 warnings: type: array description: 'Non-blocking manifest cross-validation issues (PO-186). Empty when the blob resolves cleanly; each entry is `{path, message}` keyed by `blob.…`. An inactive workflow persists even with warnings; an active save/activation is rejected with 422 instead. Returned by create/update/show, not by the list endpoint.' examples: - [] created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - id: 1 name: 'Sync orders' description: null blob_definition_version: v0-draft blob: { } active: true revision: 1 warnings: [] created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 422: description: 'Validation failed (structural, or manifest issues on an active save)' content: application/json: schema: type: object properties: message: type: string examples: - 'The given data was invalid.' errors: type: object properties: blob.nodes.0.config.credentials: type: array items: type: string examples: - - "Required config field 'credentials' is missing." examples: - message: 'The given data was invalid.' errors: blob.nodes.0.config.credentials: - "Required config field 'credentials' is missing." 429: $ref: '#/components/responses/TooManyRequests' tags: - Workflows requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Human-readable workflow name (unique per tenant). Must not be greater than 255 characters.' examples: - 'Sync orders nightly' description: type: - string - 'null' description: 'Optional free-text description.' examples: - 'Pulls orders from the upstream API every night at 02:00 UTC.' blob_definition_version: type: string description: 'Identifier of the blob schema version. See `GET /v1/schemas/workflow`.' enum: - v0-draft examples: - v0-draft blob: type: object description: 'The workflow definition payload. Must match the schema for the supplied version.' properties: { } examples: - nodes: [] edges: [] active: type: boolean description: 'Whether the workflow is active. Defaults to true when omitted.' examples: - true execution_mode: type: string description: 'Execution policy for run mode resolution. One of: async_only, sync_only, caller_decides. Defaults to async_only.' enum: - async_only - sync_only - caller_decides examples: - async_only triggers: type: array description: 'Optional set of trigger rows to sync alongside the workflow.' items: type: object properties: handle: type: string description: 'Stable UUID referenced by the workflow blob edges. Must be unique per tenant. This field is required when triggers is present. Must be a valid UUID.' examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: type: string description: 'Trigger entry-point type. One of: manual, schedule, webhook, event. This field is required when triggers is present.' enum: - manual - schedule - webhook - event examples: - webhook name: type: - string - 'null' description: 'Optional human-readable label.' examples: - 'PSP webhook' config: type: - object - 'null' description: 'Type-specific trigger configuration.' properties: { } examples: - method: POST active: type: boolean description: 'Whether the trigger is enabled. Defaults to true.' examples: - true examples: - null required: - name - blob_definition_version '/api/v1/workflows/{id}': get: summary: 'Show a workflow' operationId: showAWorkflow description: 'Returns a single workflow by id within the current tenant.' parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Success content: application/json: schema: type: object properties: id: type: integer examples: - 1 name: type: string examples: - 'Sync orders' description: type: - string - 'null' examples: - null blob_definition_version: type: string examples: - v0-draft blob: type: object properties: { } active: type: boolean examples: - true revision: type: integer examples: - 1 warnings: type: array examples: - [] created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T10:00:00Z' examples: - id: 1 name: 'Sync orders' description: null blob_definition_version: v0-draft blob: { } active: true revision: 1 warnings: [] created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T10:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Workflows put: summary: 'Replace a workflow' operationId: replaceAWorkflow description: "Performs a full-replace update of the workflow with the given id. All\ndomain fields are required. The `revision` counter is incremented\nexactly when the supplied `blob` differs from the stored one, in which\ncase the previous revision is snapshotted into the workflow history." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 200: description: Updated content: application/json: schema: type: object properties: id: type: integer examples: - 1 name: type: string examples: - 'Sync orders' description: type: - string - 'null' examples: - null blob_definition_version: type: string examples: - v0-draft blob: type: object properties: { } active: type: boolean examples: - true revision: type: integer examples: - 2 warnings: type: array examples: - [] created_at: type: string examples: - '2026-05-06T10:00:00Z' updated_at: type: string examples: - '2026-05-06T11:00:00Z' examples: - id: 1 name: 'Sync orders' description: null blob_definition_version: v0-draft blob: { } active: true revision: 2 warnings: [] created_at: '2026-05-06T10:00:00Z' updated_at: '2026-05-06T11:00:00Z' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Workflows requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Human-readable workflow name (unique per tenant). Must not be greater than 255 characters.' examples: - 'Sync orders nightly' description: type: - string - 'null' description: 'Optional free-text description.' examples: - 'Pulls orders from the upstream API every night at 02:00 UTC.' blob_definition_version: type: string description: 'Identifier of the blob schema version. See `GET /v1/schemas/workflow`.' enum: - v0-draft examples: - v0-draft blob: type: object description: 'The workflow definition payload. Must match the schema for the supplied version.' properties: { } examples: - nodes: [] edges: [] active: type: boolean description: 'Whether the workflow is active.' examples: - true execution_mode: type: string description: 'Execution policy for run mode resolution. One of: async_only, sync_only, caller_decides.' enum: - async_only - sync_only - caller_decides examples: - async_only triggers: type: array description: 'Optional set of trigger rows to sync alongside the workflow.' items: type: object properties: handle: type: string description: 'Stable UUID referenced by the workflow blob edges. Must be unique per tenant. This field is required when triggers is present. Must be a valid UUID.' examples: - 6ff8f7f6-1eb3-3525-be4a-3932c805afed type: type: string description: 'Trigger entry-point type. One of: manual, schedule, webhook, event. This field is required when triggers is present.' enum: - manual - schedule - webhook - event examples: - webhook name: type: - string - 'null' description: 'Optional human-readable label.' examples: - 'PSP webhook' config: type: - object - 'null' description: 'Type-specific trigger configuration.' properties: { } examples: - method: POST active: type: boolean description: 'Whether the trigger is enabled. Defaults to true.' examples: - true examples: - null required: - name - blob_definition_version - active - execution_mode delete: summary: 'Delete a workflow' operationId: deleteAWorkflow description: "Removes the workflow identified by `{id}` for the current tenant.\nAssociated history rows are removed via cascade." parameters: - in: header name: X-Tenant-Id description: '' schema: type: string examples: - acme-production responses: 204: description: Deleted content: text/plain: schema: type: string examples: - '' 404: description: 'Not found' content: application/json: schema: type: object properties: message: type: string examples: - 'Not found.' examples: - message: 'Not found.' 429: $ref: '#/components/responses/TooManyRequests' tags: - Workflows parameters: - in: path name: id description: 'The workflow id.' required: true schema: type: integer examples: - 1