{"openapi":"3.1.0","info":{"title":"Cascades API","version":"1.0.0","description":"# Cascades orchestration APIs\n\n# Execution model\n\nWorkflow **submission**: `POST /api/workflows/run` validates graph structure, persists a `workflow_run`\nrow with staged `task_run` rows, then either enqueues Redis/BullMQ work or executes **inline**\nwhen Redis is unavailable and `WORKFLOW_EXECUTOR_MODE` permits it.\n\n**Run lifecycle** (`WorkflowRunStatus`): `PENDING` → `RUNNING` → (`SUCCESS` | `FAILED` | `CANCELLED`).\nThe dashboard and `/api/runs/{id}/stream` SSE feed reflect authoritative state from Postgres.\n\n**Task states** (`TaskRunStatus`): `PENDING`, `QUEUED`, `RUNNING`, `SUCCESS`, `FAILED`,\n`SKIPPED`, `RETRYING`, `CANCELLED`. Tasks advance in topological waves respecting DAG edges.\n\n**Retries**: each node inherits a typed `RetryPolicy` (max retries, exponential backoff factor,\ninitial delay) parsed from persisted node JSON merged with deterministic defaults (`DEFAULT_RETRY:\nmaxRetries=0` unless overridden). Failed attempts increment `attempt` until policy exhaustion.\n\n**Timeouts**: executor wraps node work using `withTimeout`; `WorkflowNode.timeoutMs` drives the cap\n(default ~30s per persisted node defaults).\n\n**Proof lifecycle** (`ProofStatus`): `PENDING` → `SUBMITTED` → `CONFIRMED` (`FAILED`,\n`FAILED_TO_BROADCAST` on errors). Canonical payloads are hashed (`hashProofPayload`). After a DAG\nslice completes successfully a proof stub is recorded and enqueue hooks schedule **anchoring** and\nTransparency **Rekor**/Ledger follow-up workloads.\n\n**Anchor queue**: background worker (`workers/anchor-worker.ts`) processes anchor jobs produced by proof creation.\n\n**Rekor lifecycle**: Rekor-aligned jobs are queued separately (`enqueueRekorProofJob`, `rekor-worker.ts`).\nEntries become tamper-evident fingerprints suitable for auditors.\n\n**Operational visibility**: streamed `log`, `run`, and `task` SSE events align with OTLP spans and Postgres\n`execution_log` rows for traceability.\n\n---\n\n# Authentication\n\n- **Auth0-hosted login** is the default integration (`auth0.middleware`). Session-backed routes require\n  cookies issued by Auth0 middleware; call `/api/auth0/session` to hydrate the synced user row server-side.\n\n- **Local dev / Vitest bypass** (`NODE_ENV` is `development` or `test`): Auth0 is skipped by default; a deterministic mock user becomes `ADMIN`. Opt back into Auth0 with `AUTH_REQUIRED=true`.\n\n- **Explicit legacy flags**: `AUTH_DISABLED=false` / `NEXT_PUBLIC_AUTH_DISABLED=false` also disables bypass.\n\n- **API tokens**: reserved for programmatic access patterns; no bearer issuance yet—track `/api/*/security`.\n\n- **Admin routes** (`/api/internal/*`) require synced `User.role === ADMIN` resolved from Auth0 org metadata.\n\n---\n\n# Webhooks\n\n- **Stripe** (`/api/webhooks/stripe`): verified with `Stripe-Signature` using `STRIPE_WEBHOOK_SECRET`. Handles Checkout completion, invoice renewals, subscription updates/deletes.\n\n- **Completion webhooks**: optional `WORKFLOW_COMPLETION_WEBHOOK` env posts JSON summaries when workflows finish (`workflowRunId`, status, proof hash snippet). Driven from executor after terminal transitions.\n\n- **Future event contracts**: explicit signed envelopes (replay + verification payloads) planned as first-class integrations.\n\n---\n\n## SDK ↔ platform HTTP boundary\n\nThe **Workflows** and **Runs** tags below (`POST /api/workflows/run`, `GET /api/runs/{id}/stream`) define the boundary consumed by the **[cascades-sdk](https://pypi.org/project/cascades-sdk)** PyPI package (`import cascades_sdk`; source **https://github.com/no1rstack/cascades-sdk**) and the **[@noirstack/cascades-sdk](https://www.npmjs.com/package/@noirstack/cascades-sdk)** npm package (`npm i @noirstack/cascades-sdk`). The authoritative contract for this deployment is `contracts/api.yaml` in the **[Cascades platform repository](https://github.com/no1rstack/cascades/blob/main/contracts/api.yaml)**; that **[cascades-sdk](https://github.com/no1rstack/cascades-sdk)** repository publishes `cascades-sdk` to PyPI and `@noirstack/cascades-sdk` to npm and should stay aligned with that file and with `/openapi.json`.\n\n**Internal test routes** (`POST /api/integrations/*/test`, etc.) may still appear in the full `/openapi.json` for Scalar and ops tooling, but they are **omitted from `contracts/api.yaml`** so generated SDKs do not depend on dashboard-only surfaces. Keep that split when adding providers.\n\n---\n\n## Replay, verification & anchoring (roadmap)\n\nDedicated HTTP surfaces will expose deterministic replay attestations (Replay APIs tag),\nthird-party verifier handshakes (Verification APIs tag), and multi-stage anchoring (Anchoring APIs tag).\n\nExpose **proof** records today via dashboards; persistence fields align with Rekor-compatible hashes.\n\n","license":{"name":"Business Source License 1.1 (BUSL 1.1)"}},"servers":[{"url":"/","description":"This deployment’s origin (local dev or custom host)"},{"url":"https://cascades.work","description":"Public production (cascades.work)"}],"tags":[{"name":"System","description":"Public version and deployment metadata (unauthenticated reads)."},{"name":"Workflows","description":"Trigger runs and introspect deterministic plans."},{"name":"Runs","description":"Observe running graphs (streaming today; JSON snapshots planned)."},{"name":"Integrations","description":"Per-user connector credentials stored in `integration_configs` for workflow executors."},{"name":"Session","description":"Session introspection synced with Postgres."},{"name":"Account","description":"User-scoped destructive operations."},{"name":"Stripe","description":"Subscription lifecycle ingestion."},{"name":"Admin","description":"`ADMIN` workflows + reliability operators."},{"name":"Proof APIs","description":"**Experimental + roadmap.** `GET /api/workflows/{id}/proof` returns a minimal hash + execution summary; additional proof/verification HTTP exports are planned."},{"name":"Replay APIs","description":"**Reserved.** Deterministic rerun & witness playback contracts for auditors and regressions."},{"name":"Verification APIs","description":"**Reserved.** Third-party verifier hooks for remote attestation of workflow outputs."},{"name":"Anchoring APIs","description":"**Reserved.** Multi-hop anchoring attestations bridging Postgres proofs to Transparency logs."}],"paths":{"/api/workflows/run":{"post":{"operationId":"queueSampleWorkflowRun","tags":["Workflows"],"summary":"Enqueue a workflow run by catalog id","description":"Requires Auth0-backed session cookies (or mock user when AUTH_DISABLED). Persists queued run + emits execution mode cues. Optional JSON body `{ \"workflow_id\": \"<catalog-id>\", \"context\": {...} }` selects a workflow (deprecated alias `workflowId`); omit both for the bundled demo DAG. Optional `context` is arbitrary JSON merged into persisted `workflow_run.context` together with `source: \"dashboard-run\"` baseline metadata from dashboard-triggered runs. Unknown catalog ids return **404**. Staging/production tiers require Redis + worker (`ENV_TIER`). Unknown body keys return **400**.","security":[{"cookieSession":[]}],"requestBody":{"description":"Optional `{ \"workflow_id\": \"<catalog-id>\", \"context\": {...} }`. Omit body or send `{}` for the bundled demo DAG.","content":{"application/json":{"schema":{"type":"object","properties":{"workflow_id":{"type":"string","minLength":1,"description":"Catalog workflow identifier (bundled id or `db-{row}`)."},"workflowId":{"type":"string","minLength":1,"description":"Deprecated alias for `workflow_id`. If both are set, they must match. Prefer `workflow_id` in new integrations."},"context":{"type":"object","additionalProperties":{},"description":"Optional arbitrary JSON merged into persisted `workflow_run.context` together with `source: \"dashboard-run\"` defaults. Include `stripe_event_id` (evt_…) or `{ stripe_event: { id } }` to persist `workflow_run.stripe_event_id` and show Stripe → run → proof linkage."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Run accepted","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string"},"workflow_id":{"type":"string","description":"Catalog workflow DB id associated with this run (prefer this field over legacy camelCase)."},"catalogItemId":{"type":"string","description":"Catalog key used for this enqueue (`definition.id` for bundled workflows, `db-{row}` for persisted rows)."},"workflowDefinitionSnapshotId":{"type":"string","description":"FK to immutable `workflow_definition_snapshots` pinned at enqueue time."},"definitionHash":{"type":"string","description":"Structural SHA-256 of DAG + configs at enqueue (same as snapshot)."},"taskRuns":{"type":"number"},"executionMode":{"type":"string","enum":["queued","inline"],"description":"`queued`: BullMQ hand-off; `inline`: in-process run (local tier only)."},"message":{"type":"string"}},"required":["runId","workflow_id","workflowDefinitionSnapshotId","definitionHash","taskRuns","executionMode","message"]}}}},"400":{"description":"Graph validation failures","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"Workflow validation failed"},"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"404":{"description":"Catalog workflow id unknown or not owned by the caller","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"Workflow not found"},"reason":{"type":"string","enum":["not_found","invalid_definition"]},"workflow_id":{"type":"string","description":"Catalog key from the request body (or defaulted demo id when body omitted)."}},"required":["error","reason","workflow_id"]}}}},"503":{"description":"Worker queue unavailable for this environment tier","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"Execution engine unavailable"},"reason":{"type":"string","enum":["worker_queue_required"]},"message":{"type":"string"}},"required":["error","message"]}}}}}}},"/api/integrations/github":{"post":{"operationId":"saveGithubIntegration","tags":["Integrations"],"summary":"Save or update GitHub integration (PAT or secret reference)","description":"Stores a GitHub credential for the current user: either a PAT in `integration_configs`, or a `secretKey` pointing at the unified secrets table. Used by `github` workflow nodes.\n\n**Credential handling:** Inline tokens are stored per user in `integration_configs`. Secret-backed configs store only the key name; the PAT is decrypted only at execution time. The raw token is never returned (responses may include `tokenLastFour` only).\n\nMalformed JSON bodies respond with **400** and `error: \"invalid_token\"`.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":20,"maxLength":512,"description":"GitHub Personal Access Token (never echoed in responses). Mutually exclusive with secretKey."},"secretKey":{"type":"string","minLength":1,"maxLength":128,"description":"Unified secrets key (Dashboard → Secrets). Mutually exclusive with token; PAT is read at workflow execution time."},"defaultRepository":{"type":"string","pattern":"^[^/\\s]+\\/[^/\\s]+$","description":"Default repository in \"owner/repo\" format (for `repository: \"__default__\"` nodes)."}}}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string","description":"integration_configs row id"},"provider":{"type":"string","const":"github"},"tokenLastFour":{"type":"string","description":"Last four characters of the PAT for UI confirmation (inline token or decrypted secret)."},"defaultRepository":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input (token, secretKey, or repository)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token","invalid_repository","unknown_secret_key"],"description":"invalid_token: malformed body, or neither/both of token and secretKey. invalid_repository: defaultRepository. unknown_secret_key: secretKey not found for this user."}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/gitlab":{"post":{"operationId":"saveGitlabIntegration","tags":["Integrations"],"summary":"Save or update GitLab integration (PAT or secret reference)","description":"Stores a GitLab credential for the current user: either a PAT in `integration_configs`, or a `secretKey` pointing at the unified secrets table. Used by `gitlab` workflow nodes.\n\n**Credential handling:** Inline tokens are stored per user in `integration_configs`. Secret-backed configs store only the key name; the PAT is decrypted only at execution time.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":20,"maxLength":512,"description":"GitLab Personal Access Token (never echoed). Mutually exclusive with secretKey."},"secretKey":{"type":"string","minLength":1,"maxLength":128,"description":"Unified secrets key (Dashboard → Secrets). Mutually exclusive with token; PAT is read at workflow execution time."},"defaultProject":{"type":"string","pattern":"^[a-zA-Z0-9_.-]+(?:\\/[a-zA-Z0-9_.-]+)+$","description":"Default project path (for `project: \"__default__\"` nodes), e.g. `mygroup/myproject`."}}}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string","description":"integration_configs row id"},"provider":{"type":"string","const":"gitlab"},"tokenLastFour":{"type":"string"},"defaultProject":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token","invalid_project","unknown_secret_key"],"description":"invalid_token: malformed body, or neither/both of token and secretKey. invalid_project: defaultProject path. unknown_secret_key: secretKey not found for this user."}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/jira":{"post":{"operationId":"saveJiraIntegration","tags":["Integrations"],"summary":"Save or update Jira Cloud integration (API token or secret reference)","description":"Stores Jira Cloud connection: `baseUrl`, `email`, and either an inline `apiToken` or `secretKey` into unified secrets. Uses Basic auth at execution time. No OAuth.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"apiToken":{"type":"string","minLength":8,"maxLength":512},"secretKey":{"type":"string","minLength":1,"maxLength":128},"baseUrl":{"type":"string","format":"uri","pattern":"^https:\\/\\/.+","description":"Jira Cloud site URL, e.g. https://your-domain.atlassian.net"},"email":{"type":"string","format":"email","description":"Atlassian account email (Basic auth username)."}},"required":["baseUrl","email"]}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"jira"},"tokenLastFour":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token","invalid_body","unknown_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/servicenow":{"post":{"operationId":"saveServicenowIntegration","tags":["Integrations"],"summary":"Save or update ServiceNow integration (password or secret reference)","description":"Stores ServiceNow instance URL, username, and either inline `password` or `secretKey` for the unified secrets table. Basic auth at execution time. No OAuth.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string","minLength":1,"maxLength":512},"secretKey":{"type":"string","minLength":1,"maxLength":128},"instanceUrl":{"type":"string","pattern":"^https:\\/\\/[^/]+\\.service-now\\.com\\/?$","minLength":12,"maxLength":256},"username":{"type":"string","minLength":1,"maxLength":256}},"required":["instanceUrl","username"]}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"servicenow"},"passwordLastFour":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token","invalid_body","unknown_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/slack":{"post":{"operationId":"saveSlackIntegration","tags":["Integrations"],"summary":"Save or update Slack incoming webhook","description":"Stores an incoming webhook URL for `slack` workflow nodes: either paste `webhookUrl`, or save the URL in unified secrets and reference `webhookSecretKey`. Exactly one of the two is required.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"webhookUrl":{"type":"string","minLength":24,"maxLength":2048},"webhookSecretKey":{"type":"string","minLength":1,"maxLength":128,"description":"Unified secrets key whose value is the incoming webhook URL. Mutually exclusive with webhookUrl."}}}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"slack"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid webhook URL, unknown secret key, or encryption not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_webhook","unknown_webhook_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/pagerduty":{"post":{"operationId":"savePagerdutyIntegration","tags":["Integrations"],"summary":"Save PagerDuty integration (Events API v2 routing key)","description":"Stores an Events API v2 routing key inline (`routingKey`) or as a unified secrets reference (`secretKey`). No REST API keys, webhooks, or email integrations.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"routingKey":{"type":"string","minLength":10,"maxLength":512},"secretKey":{"type":"string","minLength":1,"maxLength":128}},"additionalProperties":false}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"pagerduty"},"routingKeyLastFour":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_routing_key","unknown_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/hubspot":{"post":{"operationId":"saveHubspotIntegration","tags":["Integrations"],"summary":"Save or update HubSpot integration (private app token)","description":"Stores a HubSpot private app access token inline (`privateAppToken`) or as a unified secrets reference (`secretKey`). No OAuth in this release.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"privateAppToken":{"type":"string","minLength":10,"maxLength":512},"secretKey":{"type":"string","minLength":1,"maxLength":128}},"additionalProperties":false}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"hubspot"},"tokenLastFour":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token","unknown_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/hubspot/test":{"post":{"operationId":"testHubspotIntegration","tags":["Integrations"],"summary":"Test HubSpot CRM connectivity","security":[{"cookieSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured or missing token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_configured","missing_token","provider_api_error"]},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"502":{"description":"HubSpot API error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_configured","missing_token","provider_api_error"]},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error"]}}}}}}},"/api/integrations/salesforce":{"post":{"operationId":"saveSalesforceIntegration","tags":["Integrations"],"summary":"Save or update Salesforce integration (username-password OAuth)","description":"Stores instance URL, username, security token, Connected App client id/secret, and either inline `password` or `secretKey` for the login password. OAuth authorization-code flows are not implemented in this release.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"instanceUrl":{"type":"string","format":"uri","pattern":"^https:\\/\\/.+","description":"Salesforce instance base URL, e.g. https://your-domain.my.salesforce.com"},"username":{"type":"string","minLength":1,"maxLength":256},"securityToken":{"type":"string","maxLength":128},"clientId":{"type":"string","minLength":1,"maxLength":256},"clientSecret":{"type":"string","minLength":1,"maxLength":512},"password":{"type":"string","minLength":1,"maxLength":256},"secretKey":{"type":"string","minLength":1,"maxLength":128},"loginHost":{"type":"string","format":"uri","pattern":"^https:\\/\\/.+","description":"OAuth token host (default https://login.salesforce.com). Use https://test.salesforce.com for sandboxes."}},"required":["instanceUrl","username","clientId","clientSecret"],"additionalProperties":false}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"salesforce"},"passwordLastFour":{"type":"string"},"clientSecretLastFour":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_body","unknown_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/salesforce/test":{"post":{"operationId":"testSalesforceIntegration","tags":["Integrations"],"summary":"Test Salesforce REST connectivity","security":[{"cookieSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured or missing credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_configured","missing_credentials","provider_api_error"]},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"502":{"description":"Salesforce API error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_configured","missing_credentials","provider_api_error"]},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error"]}}}}}}},"/api/integrations/vercel":{"post":{"operationId":"saveVercelIntegration","tags":["Integrations"],"summary":"Save or update Vercel integration (API token)","description":"Stores a Vercel bearer token inline (`accessToken`) or as a unified secrets reference (`secretKey`). No OAuth in this release.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string","minLength":10,"maxLength":512},"secretKey":{"type":"string","minLength":1,"maxLength":128}},"additionalProperties":false}}}},"responses":{"200":{"description":"Integration saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"provider":{"type":"string","const":"vercel"},"tokenLastFour":{"type":"string"}},"required":["ok","id","provider"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_token","unknown_secret_key"]}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"500":{"description":"Server error"}}}},"/api/integrations/vercel/test":{"post":{"operationId":"testVercelIntegration","tags":["Integrations"],"summary":"Test Vercel API connectivity","security":[{"cookieSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured or missing token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_configured","missing_token","provider_api_error"]},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not authenticated"},"502":{"description":"Vercel API error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_configured","missing_token","provider_api_error"]},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error"]}}}}}}},"/api/workflows/clone":{"post":{"operationId":"cloneCatalogWorkflow","tags":["Workflows"],"summary":"Clone a catalog workflow into a new DB-backed definition","description":"Copies the resolved DAG into a new `workflows` row owned by the caller. Bundled presets gain a fresh definition id so edits do not alter shipped templates.","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"catalogItemId":{"type":"string","minLength":1,"description":"Bundled catalog id or ``db-{workflowRowId}`` to duplicate into a new persisted workflow."}},"required":["catalogItemId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Clone persisted","content":{"application/json":{"schema":{"type":"object","properties":{"catalogItemId":{"type":"string","description":"Dashboard catalog key for the new row (always ``db-{id}``)."},"workflowRowId":{"type":"string"},"definitionId":{"type":"string","description":"New definition ``id`` embedded in the cloned JSON (distinct from bundled presets)."},"name":{"type":"string"}},"required":["catalogItemId","workflowRowId","definitionId","name"]}}}},"400":{"description":"Invalid JSON or body keys"},"401":{"description":"Unauthenticated caller"},"404":{"description":"Unknown catalog id or not permitted","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"Workflow not found"},"reason":{"type":"string","enum":["not_found","invalid_definition"]},"workflow_id":{"type":"string","description":"Catalog key from the request body (or defaulted demo id when body omitted)."}},"required":["error","reason","workflow_id"]}}}}}}},"/api/workflows/{id}/proof":{"get":{"operationId":"getWorkflowRunProofExperimental","tags":["Proof APIs"],"summary":"Workflow run proof summary (experimental)","description":"Returns proof hash and a minimal execution summary. Contract may evolve — treat as experimental.","security":[{"cookieSession":[]}],"parameters":[{"in":"path","name":"id","description":"Workflow run id (`workflow_runs.id`)","schema":{"type":"string","description":"Workflow run id (`workflow_runs.id`)"},"required":true}],"responses":{"200":{"description":"Proof summary","content":{"application/json":{"schema":{"type":"object","properties":{"experimental":{"type":"boolean","const":true},"hash":{"type":["string","null"]},"timestamp":{"type":["string","null"]},"summary":{"type":"string"},"workflowRunId":{"type":"string"},"payloadHash":{"type":["string","null"]},"proofStatus":{"type":"string"},"proofCreatedAt":{"type":["string","null"]},"proofUpdatedAt":{"type":["string","null"]},"canonicalWorkflowState":{"type":"string"},"executionSummary":{"type":"object","properties":{"workflowId":{"type":"string"},"definitionHash":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"taskCount":{"type":"number"},"tasks":{"type":"array","items":{"type":"object","properties":{"nodeKey":{"type":"string"},"canonicalState":{"type":"string"}},"required":["nodeKey","canonicalState"]}}},"required":["workflowId","definitionHash","completedAt","taskCount","tasks"]}},"required":["experimental","hash","timestamp","summary","workflowRunId","payloadHash","proofStatus","proofCreatedAt","proofUpdatedAt","canonicalWorkflowState","executionSummary"]}}}},"401":{"description":"Unauthenticated"},"404":{"description":"Run not found"}}}},"/api/system/license":{"get":{"operationId":"getLicenseStatus","tags":["Session"],"summary":"License observability (no enforcement)","description":"Returns BUSL signaling fields only — no secrets. Intended for telemetry and deployment dashboards.","responses":{"200":{"description":"License status snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["development","production"]},"hasKey":{"type":"boolean"},"envTier":{"type":"string","enum":["local","staging","production"]}},"required":["mode","hasKey","envTier"]}}}}}}},"/api/system/version":{"get":{"operationId":"getPublicApiVersion","tags":["System"],"summary":"Public HTTP API major version","description":"Machine-readable API revision label for SDK negotiators and parity checks. No authentication required.","responses":{"200":{"description":"API version identifier","content":{"application/json":{"schema":{"type":"object","properties":{"apiVersion":{"type":"string","const":"v1"}},"required":["apiVersion"]}}}}}}},"/api/proofs/verify-definition/{runId}":{"get":{"operationId":"verifyWorkflowDefinitionDrift","tags":["Proof APIs"],"summary":"Detect DAG drift relative to immutable run snapshot hash","description":"Compares structural hash pinned at enqueue (`snapshotDefinitionHash`) with re-hashed live `workflow.definition`.","security":[{"cookieSession":[]}],"parameters":[{"in":"path","name":"runId","description":"WorkflowRun id.","schema":{"type":"string","description":"WorkflowRun id."},"required":true}],"responses":{"200":{"description":"Drift verdict","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string"},"workflowId":{"type":"string"},"workflowDefinitionSnapshotId":{"type":"string"},"snapshotDefinitionHash":{"type":"string"},"currentWorkflowDefinitionHash":{"type":"string"},"driftDetected":{"type":"boolean"},"snapshotCapturedAt":{"type":"string"},"liveWorkflowUpdatedAt":{"type":"string"}},"required":["runId","workflowId","workflowDefinitionSnapshotId","snapshotDefinitionHash","currentWorkflowDefinitionHash","driftDetected","snapshotCapturedAt","liveWorkflowUpdatedAt"]}}}},"401":{"description":"Unauthenticated"},"404":{"description":"Run unseen for user or missing snapshot (legacy)."}}}},"/api/workflows/sample-plan":{"get":{"operationId":"getDeterministicSamplePlan","tags":["Workflows"],"summary":"Expose canonical workflow DAG + deterministic execution ordering","description":"Does **not** require authentication (`GET` exposes sample blueprint for tooling).","responses":{"200":{"description":"Workflow JSON + derived execution plan","content":{"application/json":{"schema":{"type":"object","properties":{"workflow":{"type":"object","additionalProperties":{}},"plan":{}},"required":["workflow"]}}}}}}},"/api/runs/{id}/stream":{"get":{"operationId":"streamRunTelemetry","tags":["Runs"],"summary":"SSE stream for status polling and log retrieval","description":"Server-sent events (`text/event-stream`). Each frame uses a fixed SSE event name (`telemetry`); the JSON body is always a **RunStreamEvent** `{ type, data }`.\n\nQuery parameters:\n\n- `since` — ISO8601 cursor into `execution_log` for incremental logs.\n\n- `taskSince` — ISO8601 cursor for `task_run` row updates.\n\nStandard `type` values (lifecycle):\n\n- `run.started` — initial run snapshot for this connection.\n\n- `run.progress` — subsequent run row snapshots.\n\n- `run.completed` / `run.failed` — terminal (stream closes).\n\nAuxiliary kinds use the same envelope: `log.entry`, `task.update`, `stream.error`.","security":[{"cookieSession":[]}],"parameters":[{"in":"path","name":"id","description":"Workflow run identifier.","schema":{"type":"string","description":"Workflow run identifier."},"required":true},{"in":"query","name":"since","description":"ISO8601 cursor into `execution_log`","schema":{"type":"string","description":"ISO8601 cursor into `execution_log`"}},{"in":"query","name":"taskSince","description":"ISO8601 cursor for `task_run` updates","schema":{"type":"string","description":"ISO8601 cursor for `task_run` updates"}}],"responses":{"200":{"description":"SSE stream. Frames named `telemetry`; parse each `data` line as RunStreamEvent.","content":{"text/event-stream":{"schema":{"type":"object","properties":{"type":{"type":"string","description":"Standard lifecycle: `run.started`, `run.progress`, `run.completed`, `run.failed`. Streams may emit `log.entry`, `task.update`, or `stream.error`."},"data":{"type":"object","additionalProperties":{},"description":"Event-specific payload (object). No ad-hoc root shapes on the wire."}},"required":["type","data"],"description":"Mandatory JSON shape for every SSE `data` line on `GET /api/runs/{id}/stream` (SSE frame name `telemetry`)."},"example":"event: telemetry\ndata: {\"type\":\"run.progress\",\"data\":{\"runId\":\"clk123\",\"status\":\"RUNNING\",\"completedAt\":null}}\n\n"}}},"401":{"description":"Unauthenticated caller"},"404":{"description":"Run absent or forbidden for requesting user"}}}},"/api/runs/{id}/logs":{"get":{"operationId":"listWorkflowRunExecutionLogs","tags":["Runs"],"summary":"List execution_log rows for a workflow run (owner-only)","description":"Keyset-paginated `execution_log` tail for a run owned by the caller. Prefer `afterCreatedAt`+`afterId` (or `beforeCreatedAt`+`beforeId`) over legacy `after`/`before` ISO cursors.","security":[{"cookieSession":[]}],"parameters":[{"in":"path","name":"id","description":"Workflow run identifier.","schema":{"type":"string","description":"Workflow run identifier."},"required":true},{"in":"query","name":"level","schema":{"type":"string","enum":["info","warn","error"]}},{"in":"query","name":"eventType","schema":{"type":"string"}},{"in":"query","name":"taskRunId","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":8000}},{"in":"query","name":"afterCreatedAt","schema":{"type":"string"}},{"in":"query","name":"afterId","schema":{"type":"string"}},{"in":"query","name":"beforeCreatedAt","schema":{"type":"string"}},{"in":"query","name":"beforeId","schema":{"type":"string"}},{"in":"query","name":"after","schema":{"type":"string"}},{"in":"query","name":"before","schema":{"type":"string"}}],"responses":{"200":{"description":"Chronological log page","content":{"application/json":{"schema":{"type":"object","properties":{"logs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string"},"eventType":{"type":"string"},"level":{"type":"string"},"message":{"type":"string"},"taskRunId":{"type":["string","null"]},"metadata":{"type":"object","additionalProperties":{}}},"required":["id","createdAt","eventType","level","message","taskRunId","metadata"]}},"meta":{"type":"object","properties":{"limit":{"type":"number"},"count":{"type":"number"},"truncated":{"type":"boolean"},"direction":{"type":"string","enum":["forward","initial","older"]},"filters":{"type":"object","properties":{"level":{"type":["string","null"]},"eventType":{"type":["string","null"]},"taskRunId":{"type":["string","null"]}},"required":["level","eventType","taskRunId"]}},"required":["limit","count","truncated","direction","filters"]}},"required":["logs","meta"]}}}},"400":{"description":"Invalid cursor combination","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"404":{"description":"Run not found for this user"}}}},"/api/system/status":{"get":{"operationId":"getSystemStatus","tags":["System"],"summary":"Deployment status (package version, tier, worker availability)","description":"Unauthenticated read of non-sensitive operational metadata for dashboards and probes. **Not part of `contracts/api.yaml`**.","responses":{"200":{"description":"Status payload","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string"},"env":{"type":"string","enum":["local","staging","production"]},"licenseMode":{"type":"string"},"workerAvailable":{"type":"boolean"}},"required":["version","env","licenseMode","workerAvailable"]}}}}}}},"/api/system/entitlements":{"get":{"operationId":"getUserEntitlements","tags":["Account"],"summary":"Current user subscription / plan entitlements","description":"Returns Stripe-derived plan fields for the authenticated user. **Not part of `contracts/api.yaml` (SDK boundary)**.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Entitlements snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string"},"status":{"type":"string","enum":["active","inactive"]},"expiresAt":{"type":["string","null"]},"lastSync":{"type":"string"}},"required":["plan","status","expiresAt","lastSync"]}}}},"401":{"description":"Unauthenticated caller"},"404":{"description":"User row missing","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}}}}},"/api/workflow-echo":{"get":{"operationId":"getWorkflowEcho","tags":["System"],"summary":"Read-only echo JSON for internal HTTP workflow demos","description":"Used by bundled `internalEcho` API nodes when `CASCADES_INTERNAL_ORIGIN` points at this deployment. **Not part of `contracts/api.yaml` (SDK boundary)**.","responses":{"200":{"description":"Echo payload","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"kind":{"type":"string","const":"cascades_workflow_echo"},"at":{"type":"string"}},"required":["ok","kind","at"]}}}}}}},"/api/secrets":{"get":{"operationId":"listUserSecrets","tags":["Account"],"summary":"List user secrets (masked values)","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Masked secret rows","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"valueMasked":{"type":"string"},"createdAt":{"type":"string"},"lastUsedAt":{"type":["string","null"]}},"required":["id","key","valueMasked","createdAt","lastUsedAt"]}}},"required":["items"]}}}},"401":{"description":"Unauthenticated caller"},"503":{"description":"Encryption not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"secrets_encryption_unconfigured"},"items":{"type":"array","maxItems":0,"minItems":0}},"required":["error","items"]}}}}}},"post":{"operationId":"upsertUserSecret","tags":["Account"],"summary":"Create or update a user secret","security":[{"cookieSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":128},"value":{"type":"string","minLength":1,"maxLength":65536}},"required":["key","value"],"additionalProperties":false}}}},"responses":{"200":{"description":"Secret stored","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"id":{"type":"string"},"key":{"type":"string"},"valueMasked":{"type":"string"},"createdAt":{"type":"string"}},"required":["ok","id","key","valueMasked","createdAt"]}}}},"400":{"description":"Invalid JSON, body, or key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"503":{"description":"Encryption not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}}}}},"/api/secrets/{id}":{"delete":{"operationId":"deleteUserSecret","tags":["Account"],"summary":"Delete a user secret by id","security":[{"cookieSession":[]}],"parameters":[{"in":"path","name":"id","description":"Secret row id","schema":{"type":"string","description":"Secret row id"},"required":true}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Invalid id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}}}}},"/api/integrations/github/test":{"post":{"operationId":"testGithubIntegrationConnectivity","tags":["Integrations"],"summary":"Verify stored GitHub credentials (GET /user)","description":"Dashboard / ops connectivity check against the upstream provider API. Some providers also publish this path in `contracts/api.yaml` for SDKs; others are OpenAPI-only.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Connectivity OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured, missing token, or client-side validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"502":{"description":"Upstream provider error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error","status","detail"]}}}}}}},"/api/integrations/gitlab/test":{"post":{"operationId":"testGitlabIntegrationConnectivity","tags":["Integrations"],"summary":"Verify stored GitLab credentials","description":"Dashboard / ops connectivity check against the upstream provider API. Some providers also publish this path in `contracts/api.yaml` for SDKs; others are OpenAPI-only.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Connectivity OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured, missing token, or client-side validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"502":{"description":"Upstream provider error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error","status","detail"]}}}}}}},"/api/integrations/jira/test":{"post":{"operationId":"testJiraIntegrationConnectivity","tags":["Integrations"],"summary":"Verify stored Jira credentials","description":"Dashboard / ops connectivity check against the upstream provider API. Some providers also publish this path in `contracts/api.yaml` for SDKs; others are OpenAPI-only.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Connectivity OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured, missing token, or client-side validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"502":{"description":"Upstream provider error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error","status","detail"]}}}}}}},"/api/integrations/servicenow/test":{"post":{"operationId":"testServicenowIntegrationConnectivity","tags":["Integrations"],"summary":"Verify stored ServiceNow credentials","description":"Dashboard / ops connectivity check against the upstream provider API. Some providers also publish this path in `contracts/api.yaml` for SDKs; others are OpenAPI-only.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Connectivity OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured, missing token, or client-side validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"502":{"description":"Upstream provider error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error","status","detail"]}}}}}}},"/api/integrations/slack/test":{"post":{"operationId":"testSlackIntegrationConnectivity","tags":["Integrations"],"summary":"Verify Slack webhook configuration","description":"Dashboard / ops connectivity check against the upstream provider API. Some providers also publish this path in `contracts/api.yaml` for SDKs; others are OpenAPI-only.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Connectivity OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured, missing token, or client-side validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"502":{"description":"Upstream provider error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error","status","detail"]}}}}}}},"/api/integrations/pagerduty/test":{"post":{"operationId":"testPagerdutyIntegrationConnectivity","tags":["Integrations"],"summary":"Verify PagerDuty routing key","description":"Dashboard / ops connectivity check against the upstream provider API. Some providers also publish this path in `contracts/api.yaml` for SDKs; others are OpenAPI-only.","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Connectivity OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]}}}},"400":{"description":"Not configured, missing token, or client-side validation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable or stable error summary."},"code":{"type":"string","description":"Stable machine code (`unauthorized`, `invalid_json`, …)."},"message":{"type":"string","description":"Optional longer explanation."},"details":{"description":"Structured diagnostics (validation, etc.)."}},"required":["error"]}}}},"401":{"description":"Unauthenticated caller"},"502":{"description":"Upstream provider error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"status":{"type":"number"},"detail":{"type":"string"}},"required":["error","status","detail"]}}}}}}},"/api/auth0/session":{"get":{"operationId":"readAuthBackedUser","tags":["Session"],"summary":"Return hydrated Postgres user synced from Auth0","responses":{"200":{"description":"`user` is `null` when unauthenticated.","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"null"}}}}}}}}},"/api/user":{"delete":{"operationId":"deleteCurrentUser","tags":["Account"],"summary":"Delete the authenticated user row","security":[{"cookieSession":[]}],"responses":{"200":{"description":"Deletion succeeded"},"401":{"description":"Not authenticated"},"500":{"description":"Database error"}}}},"/api/webhooks/stripe":{"post":{"operationId":"stripeSubscriptionWebhook","tags":["Stripe"],"summary":"Stripe Connect / Billing webhook entrypoint","description":"Requires `Stripe-Signature` header; returns `503` when Stripe client or signing secret not configured.","security":[{"stripeSignature":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string"},"data":{"type":"object","properties":{"object":{"type":"object","additionalProperties":{}}},"required":["object"]}},"required":["type","data"]}}}},"responses":{"200":{"description":"Event acknowledged"},"400":{"description":"Signature validation failed"},"503":{"description":"Stripe disabled or secret missing"}}}},"/api/replay/runs/{runId}":{"post":{"operationId":"replayRunPlaceholder","tags":["Replay APIs"],"summary":"(Roadmap) Deterministic workflow replay envelope","description":"Reserve contract for audited reruns referencing frozen DAG snapshots.","deprecated":true,"parameters":[{"in":"path","name":"runId","description":"Historical workflow_run id targeted for deterministic replay.","schema":{"type":"string","description":"Historical workflow_run id targeted for deterministic replay."},"required":true}],"responses":{"501":{"description":"Not implemented"}}}},"/api/verification/attest":{"post":{"operationId":"externalVerificationPlaceholder","tags":["Verification APIs"],"summary":"(Roadmap) Submit third-party verifier attestations","deprecated":true,"responses":{"501":{"description":"Not implemented"}}}},"/api/anchoring/transactions/{txId}":{"get":{"operationId":"anchorLookupPlaceholder","tags":["Anchoring APIs"],"summary":"(Roadmap) Follow anchor transaction lifecycle outside Postgres","description":"Connects Transparency log / Rekor UUID references back to Cascades proofs.","deprecated":true,"parameters":[{"in":"path","name":"txId","description":"External anchor transaction hash / UUID","schema":{"type":"string","description":"External anchor transaction hash / UUID"},"required":true}],"responses":{"501":{"description":"Not implemented"}}}}},"components":{"securitySchemes":{"cookieSession":{"type":"apiKey","in":"cookie","name":"__session","description":"Auth0-issued session cookie (name depends on deployment). Dev bypass uses mock user without cookies."},"stripeSignature":{"type":"apiKey","in":"header","name":"Stripe-Signature","description":"Stripe webhook signing secret validation"},"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Future explicit API tokens for automation partners."}}}}