{"openapi":"3.1.0","info":{"title":"Orchent API","version":"1.0.0","summary":"Run AI workflows as APIs, with per-tenant keys, quotas and usage.","description":"Orchent runs AI workflows as APIs. You call a flow, it runs, and you get back an\nanswer, a token count and a run id you can look up later.\n\n## Authentication\n\nTwo credentials, and which one you use depends on who is calling.\n\n* **API key** — `Authorization: Bearer ock_live_…`. This is what a backend uses.\n  Mint one at `POST /v1/keys`; the secret is shown once and stored only as a\n  hash, so it cannot be recovered afterwards.\n* **Session cookie** — `orchent_session`, set by `POST /v1/auth/login`, plus an\n  `X-CSRF-Token` header on every mutating request. This is what the Orchent web\n  app uses, and it is documented here because the account-management endpoints\n  accept nothing else: an API key can spend a tenant's quota but cannot mint\n  another key, read the billing record or start a checkout. A stolen key is then\n  a bounded problem rather than an account takeover.\n\nEvery authenticated request resolves to exactly one tenant, and every response\nis scoped to it. Asking for another tenant's run, key or flow returns `404`, not\n`403` — a distinguishable refusal would confirm the thing exists.\n\n## Errors\n\nEvery failure has the same body:\n\n```json\n{\"error\": {\"code\": \"rate_limited\", \"message\": \"…\", \"request_id\": \"…\"}}\n```\n\n`code` is from a frozen set — `invalid_request` (400), `unauthorized` (401),\n`quota_exceeded` (402), `forbidden` (403), `not_found` (404),\n`rate_limited` (429), `internal` (500) — so it is safe to branch on. `message`\nis for a human and may change. `request_id` is also returned in the\n`X-Request-Id` header on every response, including successful ones; quote it in\na support request.\n\nAn error whose cause is a *list of places* — today only a rejected flow\ndefinition — carries an additional `error.problems` array. The key is absent,\nnot empty, on errors that are one sentence.\n\n## Limits\n\n* **60 requests a minute per API key.** Over it, `429` with a `Retry-After`\n  header in seconds. The limit is per key, so splitting traffic across keys\n  splits the budget rather than multiplying it.\n* **A monthly run quota per plan** — 100 on Free, 10,000 on Pro. Over it, `402`\n  `quota_exceeded`. `GET /v1/usage` reports what you have spent.\n\n## Streaming\n\n`POST /v1/flows/{flow}/run` with `\"stream\": true` answers\n`text/event-stream` instead of JSON: a sequence of `data:` frames terminated by\n`data: [DONE]`. A failure part-way through a stream arrives as one more frame,\ncarrying the same error envelope, rather than as a status code you have already\npassed.\n","contact":{"name":"Orchent support","url":"https://www.orchent.app/docs"},"license":{"name":"Proprietary","identifier":"LicenseRef-Orchent-Commercial"}},"servers":[{"url":"https://app-staging.orchent.app","description":"The Orchent API"}],"tags":[{"name":"auth","description":"Sign up, sign in, sign out. Sets the session cookie the account-management endpoints require."},{"name":"flows","description":"The catalogue you can run, and running one. This is the surface a customer backend actually uses."},{"name":"runs","description":"What has run, how long it took and what it returned."},{"name":"usage","description":"Daily rollups and the current month against the plan quota."},{"name":"keys","description":"Mint and revoke the `ock_live_…` keys your backend authenticates with."},{"name":"secrets","description":"The credentials your flows authenticate with. The mirror image of keys: a key is something Orchent issues and shows you once, a secret is something you supply and Orchent shows you **never**. Values are write-only — no endpoint on this API returns one. Reference a secret in a flow as `{{secret:NAME}}`; Orchent supplies the value as the request leaves and it is never written into the flow."},{"name":"billing","description":"Plans, subscription state, checkout and the customer portal."},{"name":"authoring","description":"Write your own flows in the Orchent DSL: create, validate, test-run, publish. A published flow is runnable at `POST /v1/flows/{name}/run` like any other."},{"name":"templates","description":"The curated flow library, and cloning one into your account."},{"name":"account","description":"Data rights: take a copy of everything this account owns, or delete it. Session-only — exporting or deleting an entire account is not something a bearer key gets to do."},{"name":"service","description":"Liveness."}],"paths":{"/healthz":{"get":{"summary":"Liveness","operationId":"health","responses":{"200":{"description":"The gateway is up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["service"],"security":[]}},"/v1/auth/signup":{"post":{"summary":"Create an account","operationId":"signup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupBody"}}},"required":true},"responses":{"201":{"description":"Account created and signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"Invalid email, a password that does not meet the policy, a blank tenant name, or an email that is already registered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"description":"Creates a tenant and its first user, and signs that user in — the response carries `Set-Cookie: orchent_session` and `orchent_csrf`. The new tenant starts on the Free plan.","security":[]}},"/v1/auth/login":{"post":{"summary":"Sign in","operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginBody"}}},"required":true},"responses":{"200":{"description":"Signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Wrong email or password. The two are not distinguished.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"description":"Sets `orchent_session` (httpOnly) and `orchent_csrf`. Send the value of the second as `X-CSRF-Token` on every mutating request.","security":[]}},"/v1/auth/logout":{"post":{"summary":"Sign out","description":"Deletes the session server-side and clears the cookies. Sessions are server-side precisely so this can actually revoke one.","operationId":"logout","responses":{"204":{"description":"Signed out. Idempotent."},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"security":[{"session":[]}]}},"/v1/auth/verify":{"post":{"summary":"Confirm an email address","description":"Spends the token from a confirmation link. Public on purpose: the token *is* the credential, and also requiring a session would break the ordinary case of opening the link on a phone you are not signed in on.","operationId":"verifyEmail","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyBody"}}},"required":true},"responses":{"200":{"description":"Confirmed. Following an already-spent link twice is a no-op, not a second welcome.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerified"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The link is expired, already used, or not a link we issued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"security":[]}},"/v1/auth/verify/resend":{"post":{"summary":"Send a fresh confirmation link","description":"To **the signed-in user's own address**, taken from the session and not from the body — an endpoint that mails a link to whatever address it is handed is an open relay with extra steps.","operationId":"resendVerification","responses":{"200":{"description":"On its way, or `already_verified`, which is not an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSent"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Email is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"security":[{"session":[]}]}},"/v1/auth/password/forgot":{"post":{"summary":"Start a password reset","description":"**The response never varies with the answer.** This endpoint is handed an address by an anonymous caller and must not become a way to ask whether somebody has an Orchent account, so the status and body are fixed constants on every path — including when there is no such account.","operationId":"forgotPassword","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotBody"}}},"required":true},"responses":{"200":{"description":"Always this, whatever was or was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSent"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"security":[]}},"/v1/auth/password/reset":{"post":{"summary":"Set a new password from a reset link","description":"Spends the token, sets the password, and **signs the account out everywhere** — a reset exists because the account may already be in someone else's hands, and one that leaves their cookie working has not taken it back. It also confirms the address, since the link is only reachable from that inbox.","operationId":"resetPassword","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetBody"}}},"required":true},"responses":{"200":{"description":"Reset. The session cookies are cleared.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSent"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The link is no longer valid, or the new password does not meet the policy — checked first, so a rejected password does not burn the one-use link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"security":[]}},"/v1/me":{"get":{"summary":"The signed-in user and their tenant","operationId":"getMe","responses":{"200":{"description":"The caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}},"401":{"description":"No valid session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["auth"],"description":"Also refreshes the `orchent_csrf` cookie, so a client can call this on boot to recover a token it does not have.","security":[{"session":[]}]},"delete":{"summary":"Delete this account and everything under it","description":"**Irreversible, and there is no undo behind it**, so it takes two independent confirmations: the organisation's name typed out (proves you know *which* account), and the account password re-entered (proves it is you).\n\nIn order: keys are revoked and flows unpublished so the account stops serving, the subscription is cancelled, compiled flows are deleted out of the engine, and then the tenant row goes — cascading to users, keys, sessions, runs, usage, billing events, authored flows and exports. Nothing is kept.","operationId":"deleteAccount","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountBody"}}},"required":true},"responses":{"200":{"description":"Deleted. The session cookies are cleared.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletionReport"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The name or the password did not confirm.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"An API key cannot delete the account it belongs to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["account"],"security":[{"session":[]}]}},"/v1/me/export":{"get":{"summary":"List exports","operationId":"listExports","responses":{"200":{"description":"This account's exports.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportList"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["account"],"description":"Exports expire 24 hours after they are ready.","security":[{"session":[]}]},"post":{"summary":"Ask for a copy of everything this account owns","description":"`202`, not `200`: the archive is built after this returns. Poll `GET /v1/me/export/{export_id}` for the download link.","operationId":"requestExport","responses":{"202":{"description":"Accepted; the archive is being built.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["account"],"security":[{"session":[]}]}},"/v1/me/export/{export_id}":{"get":{"summary":"Poll one export","description":"Once `status` is `ready` the body carries a `download_url`.","operationId":"getExport","parameters":[{"name":"export_id","in":"path","required":true,"schema":{"type":"string","title":"Export Id"}}],"responses":{"200":{"description":"The export.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not this account's export, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["account"],"security":[{"session":[]}]}},"/v1/me/export/{export_id}/link":{"post":{"summary":"Mint a fresh download link","description":"**Rotates the token**, so the previous link stops working — which is what makes an accidentally-shared link revocable. The link carries a copy of an entire account, so it expires with the export rather than being a permanent capability.","operationId":"mintExportLink","parameters":[{"name":"export_id","in":"path","required":true,"schema":{"type":"string","title":"Export Id"}}],"responses":{"200":{"description":"The export, with a new `download_url`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The export is not ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not this account's export, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["account"],"security":[{"session":[]}]}},"/v1/me/export/{export_id}/download":{"get":{"summary":"Download the archive","description":"Gzipped JSON, `orchent.export.v1`. Two ways in and exactly two: the session cookie, or a `token` from `POST .../link`.","operationId":"downloadExport","parameters":[{"name":"export_id","in":"path","required":true,"schema":{"type":"string","title":"Export Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"The archive.","content":{"application/gzip":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such export, an expired one, or a token that does not match.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["account"],"security":[{"session":[]}]}},"/v1/keys":{"get":{"summary":"List API keys","operationId":"listKeys","responses":{"200":{"description":"The tenant's keys, revoked ones included.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}}},"401":{"description":"This endpoint requires a user session; an API key cannot mint or list keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["keys"],"description":"Secrets are never included — only the displayable prefix.","security":[{"session":[]}]},"post":{"summary":"Mint an API key","operationId":"createKey","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyBody"}}},"required":true},"responses":{"201":{"description":"Created. Save `key` now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"Missing or over-long name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["keys"],"description":"The **only** response that ever contains the secret.","security":[{"session":[]}]}},"/v1/keys/{key_id}":{"delete":{"summary":"Revoke an API key","operationId":"revokeKey","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such key for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["keys"],"description":"Immediate and idempotent. A revoked key authenticates nothing.","security":[{"session":[]}]}},"/v1/secrets":{"get":{"summary":"List your secrets","description":"Names and metadata. **Never a value** — there is no field for one and no endpoint that would return one.\n\nReadable with an API key as well as a session, because tooling needs the names; writing is session-only (below).","operationId":"listSecrets","responses":{"200":{"description":"Your secrets, without their values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretList"}}}},"401":{"description":"Bad credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["secrets"],"security":[{"apiKey":[]},{"session":[]}]},"post":{"summary":"Store a secret","description":"Session-only, like minting an API key: supplying a credential is a human action, and a leaked API key must not be able to swap the token a live flow authenticates with.\n\nA name that already exists is a `400`, not an overwrite — a silent overwrite turns a typo into a live flow quietly changing credential. Rotate with `PUT` instead.","operationId":"createSecret","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretBody"}}},"required":true},"responses":{"201":{"description":"Stored. The response carries metadata only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Secret"}}}},"401":{"description":"This endpoint requires a user session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"Bad name, empty value, or the name is already taken.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Your plan's secret limit is reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"This deployment has no secret storage configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["secrets"],"security":[{"session":[]}]}},"/v1/secrets/{name}":{"put":{"summary":"Rotate a secret","description":"Replaces the value and leaves the name alone, so every flow referencing it keeps working and picks up the new value on its next run.","operationId":"rotateSecret","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretValueBody"}}}},"responses":{"200":{"description":"Rotated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Secret"}}}},"401":{"description":"This endpoint requires a user session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"Empty or over-long value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret for this tenant, or storage is not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["secrets"],"security":[{"session":[]}]},"delete":{"summary":"Delete a secret","description":"Not blocked by a flow still referencing it. Deleting a credential is usually a response to it being compromised, and \"first edit four flows\" is the wrong answer to that — those flows fail on their next run, naming the missing secret.","operationId":"deleteSecret","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"204":{"description":"Deleted."},"401":{"description":"This endpoint requires a user session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["secrets"],"security":[{"session":[]}]}},"/v1/runs":{"get":{"summary":"List runs","operationId":"listRuns","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"A page of runs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunList"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"`limit` outside 1–100, or a malformed cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["runs"],"description":"Newest first, keyset-paginated: pass the `next_cursor` you were given back as `cursor`.","security":[{"session":[]}]}},"/v1/runs/{run_id}":{"get":{"summary":"One run, with its inputs and outputs","operationId":"getRun","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}}],"responses":{"200":{"description":"The run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDetail"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such run for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["runs"],"security":[{"session":[]}]}},"/v1/usage":{"get":{"summary":"Daily usage and the current quota","operationId":"getUsage","responses":{"200":{"description":"Buckets, totals and quota.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"A malformed day, or `from` after `to`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["usage"],"description":"`from` and `to` are inclusive `YYYY-MM-DD` days. Omit both for everything on record. `quota.runs_used` is always the current calendar month, whatever range you asked for — it is what the 402 is measured against.","security":[{"session":[]}]}},"/v1/plans":{"get":{"summary":"The plan catalogue","description":"Public — the pricing page is readable before anyone signs in. The quota each plan grants is code; the *price* is read live from the payment provider, so it cannot drift from what a customer is actually charged.","operationId":"listPlans","responses":{"200":{"description":"Every plan. `billing_enabled` is false when this deployment has no payment provider configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanList"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["billing"],"security":[]}},"/v1/billing/subscription":{"get":{"summary":"This tenant's subscription","operationId":"getSubscription","responses":{"200":{"description":"Subscription state. Answers even with billing switched off, reporting the real plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"401":{"description":"No valid session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["billing"],"security":[{"session":[]}]}},"/v1/billing/checkout":{"post":{"summary":"Start a checkout","operationId":"startCheckout","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CheckoutBody"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Redirect the browser to `checkout_url`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSession"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"That plan cannot be bought self-serve.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Billing is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["billing"],"description":"Returns a provider-hosted URL to redirect the browser to. Orchent never sees a card.\n\n**The plan changes on the webhook, never on the redirect** — the success URL is a browser navigation anyone can type, so it grants nothing.","security":[{"session":[]}]}},"/v1/billing/portal":{"post":{"summary":"Open the customer portal","operationId":"openPortal","responses":{"200":{"description":"Redirect the browser to `portal_url`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSession"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Billing is not configured, or this tenant has no billing account yet — start a checkout first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["billing"],"description":"Provider-hosted management: payment method, invoices, cancellation.","security":[{"session":[]}]}},"/v1/billing/webhook":{"post":{"summary":"Payment provider callback","description":"Not for customers to call. Documented because it is a public route and an undocumented public route is worse.\n\nUnauthenticated in the session sense and CSRF-exempt by construction: no principal is resolved, so there is no ambient credential for a cross-site form to ride on. The `Stripe-Signature` HMAC over the raw body *is* the authentication, and it is verified before the body is parsed. Always `200` once verified — an event we chose not to act on is not a delivery failure, and a non-2xx would make the provider retry for three days.","operationId":"billingWebhook","responses":{"200":{"description":"Received.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookAck"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"Signature missing, stale or forged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No webhook secret configured, so nothing can be verified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["billing"],"security":[]}},"/v1/flows":{"get":{"summary":"The flows you can run","description":"Published and enabled flows only: the platform catalogue plus your own published flows, which shadow a platform flow of the same name for you and for nobody else. A flow that exists but is unpublished is absent here and `404`s on run — indistinguishable from one that does not exist.","operationId":"listFlows","responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueFlowList"}}}},"401":{"description":"Bad credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["flows"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/flows/{flow}/run":{"post":{"summary":"Run a flow","operationId":"runFlow","parameters":[{"name":"flow","in":"path","required":true,"schema":{"type":"string","title":"Flow"}}],"responses":{"200":{"description":"The run finished.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunResult"}},"text/event-stream":{"schema":{"type":"string","description":"SSE frames, terminated by `data: [DONE]`."}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The plan's monthly run quota is spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such flow, or it is not published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["flows"],"description":"The endpoint everything else exists to support.\n\nWith `\"stream\": false` (the default) the response is JSON. With `\"stream\": true` it is `text/event-stream`: `data:` frames terminated by `data: [DONE]`, and an error part-way through arrives as one more frame carrying the usual error envelope.\n\n`{flow}` resolves **tenant-first** — your own published flow wins over a platform flow of the same name. Every run here is metered and counts against your quota.","security":[{"apiKey":[]},{"session":[]}]}},"/v1/templates":{"get":{"summary":"The template library","operationId":"listTemplates","responses":{"200":{"description":"The curated library, read-only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["templates"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/templates/{template_id}":{"get":{"summary":"One template, with its definition","operationId":"getTemplate","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"200":{"description":"The template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["templates"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/templates/{template_id}/clone":{"post":{"summary":"Clone a template into your account","description":"Creates a private **draft**, independent of the template from then on. With no `name`, a name that is already taken is auto-suffixed rather than refused.","operationId":"cloneTemplate","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CloneBody"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Your new draft.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The name you asked for is taken or not URL-safe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["templates"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/authoring/flows":{"get":{"summary":"Your flows","operationId":"listMyFlows","responses":{"200":{"description":"Your flows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowList"}}}},"401":{"description":"Bad credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"description":"Every status, without `definition`. Never an engine handle.","security":[{"apiKey":[]},{"session":[]}]},"post":{"summary":"Create a flow","operationId":"createMyFlow","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFlowBody"}}},"required":true},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The name is not URL-safe, the definition was rejected (see `error.problems`), or the plan's flow limit is reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"description":"Validates the definition, compiles it and creates it in the engine.","security":[{"apiKey":[]},{"session":[]}]}},"/v1/authoring/flows/validate":{"post":{"summary":"Validate a definition","description":"Entirely local: creates nothing, runs nothing, bills nothing. `errors` and `warnings` are objects with a `path` into the definition you sent.","operationId":"validateFlowDefinition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateBody"}}},"required":true},"responses":{"200":{"description":"The verdict. A rejected definition is still a `200` here — the request succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationResult"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/authoring/flows/{flow_id}":{"get":{"summary":"One flow, with its definition","operationId":"getMyFlow","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","title":"Flow Id"}}],"responses":{"200":{"description":"The flow.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not yours, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"security":[{"apiKey":[]},{"session":[]}]},"patch":{"summary":"Update a flow","operationId":"updateMyFlow","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","title":"Flow Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFlowBody"}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The definition was rejected — see `error.problems`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not yours, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"description":"A new `definition` is re-validated and pushed to the engine. Editing a published flow does not change what callers get until you publish again.","security":[{"apiKey":[]},{"session":[]}]},"delete":{"summary":"Delete a flow","operationId":"deleteMyFlow","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","title":"Flow Id"}}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"It is published. Unpublish first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not yours, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/authoring/flows/{flow_id}/publish":{"post":{"summary":"Publish a flow","description":"Snapshots the definition and makes the flow runnable — and billable — at `POST /v1/flows/{name}/run`. Refused while the flow is disabled.","operationId":"publishMyFlow","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","title":"Flow Id"}}],"responses":{"200":{"description":"Published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"400":{"description":"The flow is disabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not yours, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/authoring/flows/{flow_id}/unpublish":{"post":{"summary":"Unpublish a flow","description":"Takes it out of the catalogue. Callers get a `404`, as they would for a flow that never existed.","operationId":"unpublishMyFlow","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","title":"Flow Id"}}],"responses":{"200":{"description":"Unpublished.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not yours, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"security":[{"apiKey":[]},{"session":[]}]}},"/v1/authoring/flows/{flow_id}/test-run":{"post":{"summary":"Test-run a draft","description":"**Never billed**: no run row, no quota, and it works on drafts.","operationId":"testRunMyFlow","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","title":"Flow Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestRunBody"}}}},"responses":{"200":{"description":"It ran.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestRunResult"}}}},"401":{"description":"Missing, malformed or unrecognised credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited: over 60 requests a minute for this key. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something failed inside Orchent. `request_id` identifies the occurrence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not yours, or not there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["authoring"],"security":[{"apiKey":[]},{"session":[]}]}}},"components":{"schemas":{"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string","description":"The displayable head of the key, e.g. `ock_live_a1b2c3`. The secret itself is never returned after creation.","example":"ock_live_a1b2c3"},"created_at":{"type":["string","null"],"format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"}},"required":["id","name","prefix","created_at"]},"ApiKeyCreated":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key":{"type":"string","description":"**Shown once.** Orchent stores only a SHA-256 hash, so this value cannot be recovered — save it now or revoke and mint another."}},"required":["id","name","key"]},"ApiKeyList":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}},"required":["keys"]},"CatalogueFlow":{"type":"object","properties":{"name":{"type":"string","description":"What you pass as `{flow}` when you run it."},"mode":{"type":"string","description":"`chat` or `workflow`."},"description":{"type":["string","null"]},"input_schema":{"type":["object","null"],"description":"The flow's declared inputs, or `null` when its author declared none. A **declaration, not a validator** — a run whose inputs contradict it is not rejected."}},"required":["name","mode","description","input_schema"]},"CatalogueFlowList":{"type":"object","properties":{"flows":{"type":"array","items":{"$ref":"#/components/schemas/CatalogueFlow"}}},"required":["flows"]},"CheckoutBody":{"properties":{"plan":{"type":"string","maxLength":40,"title":"Plan","default":"pro"}},"type":"object","title":"CheckoutBody"},"CheckoutSession":{"type":"object","properties":{"checkout_url":{"type":"string","format":"uri"}},"required":["checkout_url"]},"CloneBody":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Name"}},"type":"object","title":"CloneBody"},"CreateFlowBody":{"properties":{"name":{"type":"string","maxLength":120,"title":"Name"},"description":{"type":"string","maxLength":500,"title":"Description","default":""},"definition":{"additionalProperties":true,"type":"object","title":"Definition"},"input_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input Schema"},"publish":{"type":"boolean","title":"Publish","default":false}},"type":"object","required":["name","definition"],"title":"CreateFlowBody"},"DeleteAccountBody":{"properties":{"confirm_name":{"type":"string","maxLength":200,"title":"Confirm Name","default":""},"password":{"type":"string","maxLength":200,"title":"Password","default":""}},"type":"object","title":"DeleteAccountBody","description":"Two independent confirmations, both required. See\n`data_rights.confirmation_problem` for why neither alone is enough."},"DeletionReport":{"type":"object","properties":{"tenant_id":{"type":"string"},"tenant_name":{"type":"string"},"deleted":{"type":"boolean","description":"Whether the tenant row is gone."},"rows_deleted":{"type":"object","description":"What was there, per table."},"rows_remaining":{"type":"object","description":"What is still there, per table. Empty is the expected answer; anything in it is a cascade that did not fire."},"keys_revoked":{"type":"integer"},"flows_unpublished":{"type":"integer"},"sessions_ended":{"type":"integer"},"engine_flows":{"type":"object","description":"Compiled flows removed from the engine."},"subscription":{"type":["object","null"],"description":"What happened to the subscription, if there was one."}},"required":["tenant_id","deleted","rows_deleted","rows_remaining"]},"EmailSent":{"type":"object","properties":{"status":{"type":"string","enum":["sent","already_verified","reset"],"description":"Deliberately coarse. Whether the provider accepted the message is not reported, because the customer cannot act on the difference and the honest next step either way is to look in their inbox."},"message":{"type":"string","description":"Text safe to show the customer."}},"required":["status"]},"EmailVerified":{"type":"object","properties":{"verified":{"type":"boolean","const":true},"email":{"type":"string"}},"required":["verified","email"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","quota_exceeded","forbidden","not_found","rate_limited","internal"],"description":"A frozen machine code. Branch on this, not on `message`."},"message":{"type":"string","description":"Human text. May change."},"request_id":{"type":"string","description":"Also in the `X-Request-Id` header."},"problems":{"type":"array","items":{"$ref":"#/components/schemas/Problem"},"description":"Present only when the cause is a list of places — today, a rejected flow definition. Absent otherwise."}},"required":["code","message","request_id"]}},"required":["error"]},"Export":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","ready","failed","expired"],"description":"`pending` until the archive is built; `expired` after 24 hours."},"requested_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"size_bytes":{"type":["integer","null"]},"counts":{"type":["object","null"],"description":"Rows per table in the archive — what you are about to download."},"error":{"type":"string","description":"Present only on `failed`, and deliberately one fixed sentence."},"download_url":{"type":"string","description":"Present once `ready`. From the poll endpoint it is the session-authenticated path; from `POST .../link` it carries a time-limited token instead."}},"required":["id","status","requested_at"]},"ExportList":{"type":"object","properties":{"exports":{"type":"array","items":{"$ref":"#/components/schemas/Export"}}},"required":["exports"]},"Flow":{"type":"object","properties":{"id":{"type":"string","example":"tflow_c1e2dbb1fca74c50ab243090f9f8f55b"},"slug":{"type":"string","description":"Identical to `name`. Served as an explicit alias so no client ever slugifies anything."},"name":{"type":"string","description":"Lowercase letters, digits and hyphens, 2–64 characters. URL-safe by construction, because it is what you call.","pattern":"^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$"},"description":{"type":["string","null"]},"mode":{"type":"string"},"status":{"type":"string","enum":["draft","published"]},"enabled":{"type":"boolean"},"runnable":{"type":"boolean","description":"Published **and** enabled: whether `POST /v1/flows/{name}/run` will find it."},"input_schema":{"type":["object","null"]},"template_id":{"type":["string","null"]},"node_count":{"type":"integer"},"published_version":{"type":["integer","null"]},"published_at":{"type":["string","null"],"format":"date-time"},"has_unpublished_changes":{"type":"boolean"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"},"definition":{"type":["object","null"],"description":"The Orchent DSL graph. Present everywhere except the listing."}},"required":["id","slug","name","mode","status","enabled","runnable","node_count","created_at","updated_at"]},"FlowList":{"type":"object","properties":{"flows":{"type":"array","items":{"$ref":"#/components/schemas/Flow"}}},"required":["flows"]},"ForgotBody":{"properties":{"email":{"type":"string","maxLength":320,"minLength":3,"title":"Email"}},"type":"object","required":["email"],"title":"ForgotBody"},"Health":{"type":"object","properties":{"status":{"type":"string","const":"ok"}},"required":["status"]},"Identity":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"tenant":{"$ref":"#/components/schemas/Tenant"}},"required":["user","tenant"]},"KeyBody":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"}},"type":"object","required":["name"],"title":"KeyBody"},"LoginBody":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginBody"},"Plan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"tagline":{"type":"string"},"features":{"type":"array","items":{"type":"string"}},"runs_limit":{"type":["integer","null"],"description":"Runs a month. `null` means unmetered."},"purchasable":{"type":"boolean","description":"Whether `POST /v1/billing/checkout` will sell it. False for every plan when billing is not configured on this deployment."},"price":{"$ref":"#/components/schemas/Price"}},"required":["id","name","tagline","features","runs_limit","purchasable","price"]},"PlanList":{"type":"object","properties":{"billing_enabled":{"type":"boolean"},"plans":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}},"required":["billing_enabled","plans"]},"PortalSession":{"type":"object","properties":{"portal_url":{"type":"string","format":"uri"}},"required":["portal_url"]},"Price":{"type":["object","null"],"description":"Read live from the payment provider; `null` when there is nothing to read it from. The amount is never written down in Orchent.","properties":{"amount_cents":{"type":["integer","null"]},"currency":{"type":"string","example":"usd"},"interval":{"type":"string","example":"month"}}},"Problem":{"type":"object","properties":{"code":{"type":"string","description":"e.g. `unknown_node_type`, `host_not_allowed`."},"message":{"type":"string"},"path":{"type":["string","null"],"description":"Where in the submitted definition, e.g. `nodes[2].config.url`."},"node":{"type":["string","null"]}},"required":["code","message"]},"Quota":{"type":"object","properties":{"runs_limit":{"type":["integer","null"],"description":"`null` on an unmetered plan."},"runs_used":{"type":"integer","description":"Runs so far this calendar month."}},"required":["runs_limit","runs_used"]},"ResetBody":{"properties":{"token":{"type":"string","maxLength":512,"minLength":1,"title":"Token"},"password":{"type":"string","title":"Password"}},"type":"object","required":["token","password"],"title":"ResetBody"},"RunDetail":{"type":"object","properties":{"id":{"type":"string"},"flow":{"type":"string"},"status":{"type":"string"},"duration_ms":{"type":["integer","null"]},"tokens":{"type":["integer","null"]},"created_at":{"type":["string","null"],"format":"date-time"},"streamed":{"type":"boolean"},"inputs":{},"outputs":{},"error":{"type":["string","null"]}},"required":["id","flow","status","created_at","streamed"]},"RunList":{"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/RunSummary"}},"next_cursor":{"type":["string","null"],"description":"Pass back as `cursor` for the next page. `null` on the last one."}},"required":["runs","next_cursor"]},"RunResult":{"type":"object","properties":{"run_id":{"type":"string"},"answer":{"type":["string","null"]},"outputs":{},"tokens":{"type":["integer","null"]},"duration_ms":{"type":["integer","null"]}},"required":["run_id"]},"RunSummary":{"type":"object","properties":{"id":{"type":"string"},"flow":{"type":"string"},"status":{"type":"string","description":"`succeeded` or `failed`."},"duration_ms":{"type":["integer","null"]},"tokens":{"type":["integer","null"]},"created_at":{"type":["string","null"],"format":"date-time"}},"required":["id","flow","status","created_at"]},"Secret":{"type":"object","properties":{"name":{"type":"string","description":"How a flow refers to it: `{{secret:NAME}}`. `[A-Za-z][A-Za-z0-9_.-]{0,63}`.","example":"GITHUB_TOKEN"},"fingerprint":{"type":"string","description":"A short **keyed, one-way** digest of the value — not a prefix of it and not reversible. It exists so you can tell whether a rotation landed, and whether this is the same value as the one in your CI, without the value ever crossing the wire.","example":"0639111a"},"description":{"type":"string"},"key_version":{"type":"integer","description":"Which generation of the encryption key seals this value. Bookkeeping for key rotation; nothing a caller acts on."},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"},"created_by":{"type":["string","null"]},"updated_by":{"type":["string","null"]},"last_used_at":{"type":["string","null"],"format":"date-time"}},"required":["name","fingerprint","description","created_at","updated_at"]},"SecretBody":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name"},"value":{"type":"string","minLength":1,"title":"Value"},"description":{"type":"string","maxLength":200,"title":"Description","default":""}},"type":"object","required":["name","value"],"title":"SecretBody"},"SecretList":{"type":"object","properties":{"secrets":{"type":"array","items":{"$ref":"#/components/schemas/Secret"}},"limit":{"type":["integer","null"],"description":"How many your plan allows; `null` is unmetered."},"enabled":{"type":"boolean","description":"`false` on a deployment with no encryption key configured. Reads still answer; every write returns `404` with an explanation."}},"required":["secrets","limit","enabled"]},"SecretValueBody":{"properties":{"value":{"type":"string","minLength":1,"title":"Value"},"description":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Description"}},"type":"object","required":["value"],"title":"SecretValueBody","description":"A rotation. The name is in the path; only the value changes."},"SignupBody":{"properties":{"email":{"type":"string","maxLength":320,"minLength":3,"title":"Email"},"password":{"type":"string","title":"Password"},"tenant_name":{"type":"string","maxLength":200,"minLength":1,"title":"Tenant Name"}},"type":"object","required":["email","password","tenant_name"],"title":"SignupBody"},"Subscription":{"type":"object","properties":{"plan":{"type":"string"},"status":{"type":["string","null"],"description":"The provider's status verbatim (`active`, `trialing`, `past_due`, `unpaid`, `canceled`, …), or `null` if there has never been one."},"current_period_end":{"type":["string","null"],"format":"date-time"},"customer_id":{"type":["string","null"]},"billing_enabled":{"type":"boolean"},"manageable":{"type":"boolean","description":"Whether `POST /v1/billing/portal` will work — it needs a billing account to point at."},"needs_attention":{"type":"boolean","description":"True for the states a customer has to act on, so a client does not have to learn the provider's vocabulary."}},"required":["plan","status","billing_enabled","manageable","needs_attention"]},"Template":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"category":{"type":["string","null"]},"mode":{"type":"string"},"input_schema":{"type":["object","null"]},"version":{"type":"integer"},"definition":{"type":["object","null"],"description":"Present on the detail endpoint only."}},"required":["id","name","mode","version"]},"TemplateList":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}},"required":["templates"]},"Tenant":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"plan":{"type":"string","description":"`free`, `pro` or `unlimited`."}},"required":["id","name","plan"]},"TestRunBody":{"properties":{"inputs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Inputs"},"query":{"type":"string","title":"Query","default":""},"stream":{"type":"boolean","title":"Stream","default":false}},"type":"object","title":"TestRunBody"},"TestRunResult":{"type":"object","properties":{"flow":{"type":"string"},"answer":{"type":["string","null"]},"outputs":{},"tokens":{"type":["integer","null"]},"duration_ms":{"type":["integer","null"]},"billed":{"type":"boolean","description":"Always `false`. A test run creates no run row and spends no quota.","const":false}},"required":["flow","billed"]},"UpdateFlowBody":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description"},"definition":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Definition"},"input_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input Schema"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"}},"type":"object","title":"UpdateFlowBody"},"Usage":{"type":"object","properties":{"buckets":{"type":"array","items":{"$ref":"#/components/schemas/UsageBucket"}},"totals":{"type":"object","properties":{"runs":{"type":"integer"},"tokens":{"type":"integer"}},"required":["runs","tokens"]},"quota":{"$ref":"#/components/schemas/Quota"}},"required":["buckets","totals","quota"]},"UsageBucket":{"type":"object","properties":{"date":{"type":"string","format":"date"},"runs":{"type":"integer"},"tokens":{"type":"integer"}},"required":["date","runs","tokens"]},"User":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"}},"required":["id","email"]},"ValidateBody":{"properties":{"definition":{"additionalProperties":true,"type":"object","title":"Definition"}},"type":"object","required":["definition"],"title":"ValidateBody"},"ValidationResult":{"type":"object","properties":{"ok":{"type":"boolean"},"errors":{"type":"array","items":{"$ref":"#/components/schemas/Problem"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Problem"}}},"required":["ok","errors","warnings"]},"VerifyBody":{"properties":{"token":{"type":"string","maxLength":512,"minLength":1,"title":"Token"}},"type":"object","required":["token"],"title":"VerifyBody"},"WebhookAck":{"type":"object","properties":{"received":{"type":"boolean"},"outcome":{"type":"string","enum":["applied","duplicate","ignored","unattributed"]}},"required":["received","outcome"]}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer ock_live_…`. Mint one at `POST /v1/keys`."},"session":{"type":"apiKey","in":"cookie","name":"orchent_session","description":"Set by `POST /v1/auth/login`. Mutating requests must also send the `X-CSRF-Token` header, whose value is the `orchent_csrf` cookie."}}}}