{
  "openapi": "3.0.3",
  "info": {
    "title": "Cascades API",
    "version": "1.0.0",
    "description": "Public REST + webhook surface for triggering cascades, reading execution\nstate, and subscribing to lifecycle events.\n\nThis document is the single source of truth for the reference at\n[/docs](/docs): every operation carries an `operationId` (the slug of its\nreference page), the scopes it enforces (`x-scopes`), whether it is\nidempotent (`x-idempotent`), how to poll it when it is asynchronous\n(`x-async`), and — for the nine operations the MCP server wraps — the\nagent-facing tool definition (`x-mcp`). Webhook events you RECEIVE are\ncatalogued under the root `x-webhooks` key.\n\n## Recommended integration order\n\n1. `POST /webhook_subscriptions` — subscribe your endpoint to lifecycle\n   events. Store the `signing_secret` from the response; it is shown\n   exactly once.\n2. `POST /workflows/:id/trigger` — start a run with a unique\n   `Idempotency-Key` and your `external_reference`.\n3. `GET /executions/:id` — poll until `status` leaves\n   `pending` / `running` / `waiting`. Cascades is asynchronous;\n   `trigger` returns `202 Accepted`, not the final state.\n4. Receive signed events at your subscriber URL: `adverse_action.*`\n   (triggered / completed / failed / retry_scheduled) and `execution.*`\n   (completed / failed). Verify `X-Cascades-Signature` before trusting\n   the payload.\n5. (If the workflow includes Human Review) `POST /executions/:id/review_decisions`\n   to approve or deny a paused execution.\n\n## Authentication\n\nEvery request requires a bearer token. Click **Authorize** above and\npaste your token. Tokens are issued per `ApiClient` and carry a\nscope vocabulary: `workflows:read`, `workflows:write`,\n`workflow_templates:read`, `executions:read`, `executions:read:full`,\n`executions:write`, `webhooks:read`, `webhooks:write`. Each operation\nbelow lists the scope it enforces; a token without it receives\n`403 insufficient_scope`. See\n[/docs/concepts/scopes-and-keys](/docs/concepts/scopes-and-keys).\n\n## Errors\n\nEvery non-2xx response is a JSON envelope:\n`{ \"error\": { \"code\", \"message\", \"details\": [], \"request_id\", \"doc_url\" } }`.\n`code` is a stable machine-readable string; `doc_url` links to the\nreference page for that code at `/docs/api/errors/<code>`.\n\n## Rate limits\n\n- **60 req/min per source IP** on `/api/v1/*`\n- **600 req/hour per token prefix** (first 8 chars of bearer)\n- **300 req/5min per IP** globally\n\nA throttled request receives `429` with the standard JSON error\nenvelope (`code: rate_limited`) and a `Retry-After` header giving the\nnumber of seconds until the window resets. Honour `Retry-After`; when\nit is absent back off exponentially with a minimum 1-second wait.\n\n## Receiving webhooks\n\nCascades POSTs lifecycle events to each active subscription whose\n`event_filter` matches (an empty filter receives every event). The\nbody is a `WebhookEnvelope`: `{ \"id\", \"event\", \"created_at\", \"data\" }`,\nwhere `id` is a UUID shared by every subscriber's copy of the same\nevent (use it to de-duplicate). Per-event `data` shapes are documented\nunder `x-webhooks` below.\n\n**Headers on every delivery**\n\n- `X-Cascades-Event` — the event name, e.g. `execution.completed`\n- `X-Cascades-Delivery` — the `WebhookDelivery` id (see `GET /webhook_deliveries/{id}`)\n- `X-Cascades-Signature` — `t=<unix timestamp>,v1=<hex hmac>[,v0=<hex hmac>]`\n- `User-Agent: Cascades-Webhook/1.0`\n\n**Signature scheme.** `v1` is `HMAC-SHA256(signing_secret, \"<t>.<raw body>\")`\nencoded as lowercase hex, where `<t>` is the integer timestamp from the\nsame header and `<raw body>` is the exact request body bytes (do not\nre-serialise the JSON before hashing). Compare with a constant-time\nequality check and reject timestamps older than a few minutes to defeat\nreplay. After `POST /webhook_subscriptions/{id}/rotate_secret` the\nprevious secret keeps signing for a **24-hour grace window**: during\nthat window deliveries carry both `v1=` (new secret) and `v0=` (previous\nsecret), so verify against whichever secret you currently hold.\n\n**Retries.** A delivery counts as successful on any 2xx response\n(5 s connect / 10 s read timeout). Anything else is retried on a fixed\nschedule of **60 s, 300 s, 1 800 s, 7 200 s, 28 800 s, 86 400 s**\n(1 m, 5 m, 30 m, 2 h, 8 h, 24 h) — six attempts in total — after which\nthe delivery is marked `dead`. Dead and failed deliveries can be\nre-queued manually with `POST /webhook_deliveries/{id}/retry`.\nSubscriber URLs must be publicly routable (no private, loopback or\ncloud-metadata addresses); the check runs at save time and again at\ndelivery time.\n\n## Try-it-now safety\n\nTry-it-now requests hit **whichever environment served this\ndocs page** — same origin, no dropdown to mismatch. In any\nenvironment Cascades runs, vendor calls (Equifax, LexisNexis)\nare mocked at the deployment level (`CRS_EXECUTION_MODE=mock`),\nso triggering a workflow does NOT pull a real credit report.\n**However:** mailer nodes (`SendAdverseAction`, `SendEmail`),\nSlack notifications, and outbound `Webhook` action nodes do\nexecute for real, and lifecycle webhooks (`adverse_action.*`,\n`execution.*`) are POSTed to subscriber URLs as configured.\nUse a workflow whose recipients and webhook targets you control\nduring a demo click-through. (In development, mailers go to\nletter_opener at `/letter_opener` instead of SMTP.)\n"
  },
  "servers": [
    {
      "url": "/api/v1",
      "description": "Current host"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "uuid"
      }
    },
    "parameters": {
      "PageParam": {
        "name": "page",
        "in": "query",
        "description": "1-based page number.",
        "schema": {
          "type": "integer",
          "default": 1,
          "minimum": 1
        }
      },
      "PerPageParam": {
        "name": "per_page",
        "in": "query",
        "description": "Items per page. Values above 100 are clamped to 100.",
        "schema": {
          "type": "integer",
          "default": 25,
          "minimum": 1,
          "maximum": 100
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "maxLength": 200
        },
        "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "description": "Unique per-business-event key. Identical resubmissions return\nthe original response; conflicting bodies return `409\nidempotency_conflict`. Concurrent identical requests are\nrace-safe. A missing or blank header returns\n`422 missing_idempotency_key`.\n\nThe header is accepted up to 200 characters, but the stored key is\nnamespaced to your client (`api_client:<id>:<key>`, and for\nre-evaluations `api_client:<id>:reeval:<execution_id>:<key>`) and\nthe stored value is capped at 200 characters — so the **effective\nlimit on `trigger` is about 185 characters** and lower still on\n`re_evaluations`. Use a UUID (36 characters) or another key of at\nmost 128 characters to stay clear of the cap.\n"
      },
      "WorkflowIdParam": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Workflow id. Only workflows owned by the token's account resolve; a foreign or deleted id returns `404`.",
        "schema": {
          "type": "integer"
        }
      },
      "ExecutionIdParam": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Execution id. Scoped to the calling api_client; a foreign id returns `404`.",
        "schema": {
          "type": "integer"
        }
      },
      "BatchIdParam": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Batch id. Scoped to the calling api_client; a foreign id returns `404`.",
        "schema": {
          "type": "integer"
        }
      },
      "ArtifactIdParam": {
        "name": "artifact_id",
        "in": "path",
        "required": true,
        "description": "Artifact id as returned by the matching list endpoint.",
        "schema": {
          "type": "integer"
        }
      },
      "WebhookSubscriptionIdParam": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Webhook subscription id. Scoped to the calling api_client.",
        "schema": {
          "type": "integer"
        }
      },
      "WebhookDeliveryIdParam": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Webhook delivery id. Scoped to the calling api_client's subscriptions.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "schemas": {
      "Meta": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "example": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
            "description": "Correlation id for this request. Also sent as the `X-Request-Id` response header; quote it when contacting support."
          }
        },
        "required": [
          "request_id"
        ]
      },
      "PaginatedMeta": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Meta"
          },
          {
            "type": "object",
            "properties": {
              "page": {
                "type": "integer",
                "example": 1
              },
              "per_page": {
                "type": "integer",
                "example": 25
              },
              "total_count": {
                "type": "integer",
                "example": 142
              },
              "total_pages": {
                "type": "integer",
                "example": 6
              },
              "object": {
                "type": "string",
                "example": "execution",
                "description": "The object type of every item in `data`."
              }
            },
            "required": [
              "page",
              "per_page",
              "total_count",
              "total_pages",
              "object"
            ]
          }
        ]
      },
      "StaticCollectionMeta": {
        "description": "Meta for collections that are NOT paginated (artifact lists). The\nwhole collection is always returned in one response and the\n`page` / `per_page` query parameters are ignored. The pagination\nfields are still present for shape compatibility but hold constant\nvalues: `page` is always 1, `total_pages` is always 1, and\n`per_page` equals `total_count`.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Meta"
          },
          {
            "type": "object",
            "properties": {
              "object": {
                "type": "string",
                "example": "report_artifact"
              },
              "total_count": {
                "type": "integer",
                "example": 2,
                "description": "Number of items in `data`."
              },
              "page": {
                "type": "integer",
                "enum": [
                  1
                ],
                "description": "Always 1."
              },
              "per_page": {
                "type": "integer",
                "description": "Always equal to `total_count`."
              },
              "total_pages": {
                "type": "integer",
                "enum": [
                  1
                ],
                "description": "Always 1."
              }
            },
            "required": [
              "object",
              "total_count"
            ]
          }
        ]
      },
      "ErrorEnvelope": {
        "type": "object",
        "description": "The envelope returned by every non-2xx response, including `429` and the `/api/*` 404 catch-all.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "validation_failed",
                "description": "Stable machine-readable error code. Branch on this, never on `message`."
              },
              "message": {
                "type": "string",
                "example": "The request could not be processed."
              },
              "details": {
                "type": "array",
                "description": "Field-level detail when available; otherwise an empty array.",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string",
                      "example": "execution_mode"
                    },
                    "message": {
                      "type": "string",
                      "example": "must be test or live"
                    }
                  }
                }
              },
              "request_id": {
                "type": "string",
                "example": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
              },
              "doc_url": {
                "type": "string",
                "format": "uri",
                "description": "Link to the reference page for this error code",
                "example": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "CascadeDefinition": {
        "type": "array",
        "description": "A cascade's node graph: an ordered array of `CascadeNode` objects.\nExactly one `ApplicantSource` node is the entry point; every other\nnode is reached through the edge keys (`next_step`, `pass_steps`,\n`human_review_steps`, `fail_steps`), each of which is an array of\nnode `id`s that must resolve within the same array. An empty array\nis accepted on create/update (an empty draft) but cannot be run.\n",
        "items": {
          "$ref": "#/components/schemas/CascadeNode"
        },
        "example": [
          {
            "id": "step_applicant",
            "type": "ApplicantSource",
            "name": "Applicant",
            "next_step": [
              "step_credit"
            ]
          },
          {
            "id": "step_credit",
            "type": "BillingProduct",
            "name": "Credit report",
            "billing_product_code": "EQF1003",
            "next_step": [
              "step_score_check"
            ]
          },
          {
            "id": "step_score_check",
            "type": "IfElse",
            "name": "Score at least 650?",
            "branches": {
              "pass": {
                "group_logic": "and",
                "condition_groups": [
                  {
                    "logic": "and",
                    "conditions": [
                      {
                        "source_node_id": "step_credit",
                        "field": "creditScore.fico9",
                        "operator": "greater_than_or_equal",
                        "value": 650
                      }
                    ]
                  }
                ]
              }
            },
            "pass_steps": [
              "step_approve"
            ],
            "fail_steps": [
              "step_adverse_action"
            ]
          },
          {
            "id": "step_approve",
            "type": "Action",
            "name": "Approve",
            "action_type": "Approve"
          },
          {
            "id": "step_adverse_action",
            "type": "Action",
            "name": "Adverse action",
            "action_type": "AdverseAction",
            "adverse_action": {
              "recipient": "{{applicant.email}}",
              "action_taken": "denied",
              "send_strategy": "draft_first",
              "reason": "Credit score below the minimum required for this property."
            }
          }
        ]
      },
      "CascadeNode": {
        "type": "object",
        "description": "One node of a cascade. `id` and `type` are always required; the\nremaining required keys depend on `type` (see `x-required-keys-by-type`):\n\n| type | required keys | purpose |\n|---|---|---|\n| `ApplicantSource` | — | Entry point; exposes the trigger payload as `context.applicant`. Exactly one per cascade. |\n| `BillingProduct` (alias `Product`) | `billing_product_code` | Pulls a report (credit, criminal, eviction, identity, income). |\n| `IfElse` (alias `Condition`) | `branches` | Evaluates conditions and routes to `pass_steps`, `human_review_steps` or `fail_steps`. |\n| `Wait` | `wait` | Pauses for `wait.duration` × `wait.unit`. |\n| `GoTo` | `target_step` | Runtime jump to another node (not a graph edge). |\n| `ShowResults` | — | Terminal node that surfaces collected outputs. |\n| `Action` | `action_type` | `Approve`, `SendEmail` (reads `email`), `AdverseAction` (reads `adverse_action`), `InternalNotification` (reads `internal_notification`). |\n\n`Product` and `Condition` are legacy aliases the engine still resolves;\nnew definitions should use `BillingProduct` and `IfElse`. Unknown\nkeys are tolerated and ignored by the engine.\n",
        "required": [
          "id",
          "type"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique within the definition, e.g. `step_credit`. Edge keys reference nodes by this value.",
            "example": "step_credit"
          },
          "type": {
            "type": "string",
            "enum": [
              "ApplicantSource",
              "BillingProduct",
              "Product",
              "IfElse",
              "Condition",
              "Wait",
              "GoTo",
              "ShowResults",
              "Action"
            ]
          },
          "name": {
            "type": "string",
            "description": "Human-readable label shown in the builder and in `step_outputs`.",
            "example": "Credit report"
          },
          "next_step": {
            "type": "array",
            "description": "Default forward edge — ids of the node(s) to run next.",
            "items": {
              "type": "string"
            }
          },
          "billing_product_code": {
            "type": "string",
            "description": "`BillingProduct` / `Product` only. The product to pull, e.g. `EQF1003` (Equifax credit), `TUC1001` (TransUnion credit), `CIC4002` (criminal), `CIC4003` (eviction). May also be nested as `config.billing_product_code`.",
            "example": "EQF1003"
          },
          "config": {
            "type": "object",
            "description": "Optional per-node configuration. `store_result_as` names the key under which a product's output is stored.",
            "properties": {
              "store_result_as": {
                "type": "string"
              },
              "billing_product_code": {
                "type": "string",
                "description": "Alternative location for `billing_product_code`."
              }
            },
            "additionalProperties": true
          },
          "branches": {
            "type": "object",
            "description": "`IfElse` / `Condition` only. Conditions for the `pass` branch and (optionally) the `human_review` branch; anything that matches neither takes `fail_steps`.",
            "properties": {
              "pass": {
                "$ref": "#/components/schemas/CascadeBranch"
              },
              "human_review": {
                "$ref": "#/components/schemas/CascadeBranch"
              }
            }
          },
          "pass_steps": {
            "type": "array",
            "description": "`IfElse` only. Node ids to run when the `pass` branch matches.",
            "items": {
              "type": "string"
            }
          },
          "human_review_steps": {
            "type": "array",
            "description": "`IfElse` only. Node ids to run when the `human_review` branch matches; the execution pauses with `needs_human_review: true` until a review decision resumes it.",
            "items": {
              "type": "string"
            }
          },
          "fail_steps": {
            "type": "array",
            "description": "`IfElse` only. Node ids to run when no branch matches.",
            "items": {
              "type": "string"
            }
          },
          "action_type": {
            "type": "string",
            "description": "`Action` only.",
            "enum": [
              "Approve",
              "SendEmail",
              "AdverseAction",
              "InternalNotification"
            ]
          },
          "email": {
            "type": "object",
            "description": "`Action` with `action_type: SendEmail`. Fields support `{{variable}}` interpolation from the execution context.",
            "properties": {
              "to": {
                "type": "string"
              },
              "subject": {
                "type": "string"
              },
              "body": {
                "type": "string"
              }
            },
            "additionalProperties": true
          },
          "adverse_action": {
            "type": "object",
            "description": "`Action` with `action_type: AdverseAction`. `send_strategy: draft_first` parks the notice for human review before sending.",
            "properties": {
              "recipient": {
                "type": "string",
                "example": "{{applicant.email}}"
              },
              "action_taken": {
                "type": "string",
                "enum": [
                  "denied",
                  "revoked",
                  "adverse_employment",
                  "other_action"
                ],
                "description": "The action box ticked on the notice: `denied` (application denied), `revoked` (credit revoked or terms changed unfavorably), `adverse_employment` (adverse employment action) or `other_action`. Cascades the AI designer generates always use `denied`."
              },
              "send_strategy": {
                "type": "string",
                "enum": [
                  "draft_first",
                  "auto_send"
                ]
              },
              "reason": {
                "type": "string"
              }
            },
            "additionalProperties": true
          },
          "internal_notification": {
            "type": "object",
            "description": "`Action` with `action_type: InternalNotification`. Operator-facing notification settings.",
            "additionalProperties": true
          },
          "wait": {
            "type": "object",
            "description": "`Wait` only. The delay before the next node runs.",
            "properties": {
              "duration": {
                "type": "number",
                "example": 24
              },
              "unit": {
                "type": "string",
                "enum": [
                  "seconds",
                  "minutes",
                  "hours",
                  "days",
                  "weeks"
                ],
                "example": "hours"
              }
            }
          },
          "target_step": {
            "type": "string",
            "description": "`GoTo` only. The node id to jump to."
          }
        },
        "additionalProperties": true,
        "x-required-keys-by-type": {
          "ApplicantSource": [],
          "BillingProduct": [
            "billing_product_code"
          ],
          "Product": [
            "billing_product_code"
          ],
          "IfElse": [
            "branches"
          ],
          "Condition": [
            "branches"
          ],
          "Wait": [
            "wait"
          ],
          "GoTo": [
            "target_step"
          ],
          "ShowResults": [],
          "Action": [
            "action_type"
          ]
        }
      },
      "CascadeBranch": {
        "type": "object",
        "description": "A branch of an `IfElse` node — one or more condition groups combined with `group_logic`.",
        "properties": {
          "group_logic": {
            "type": "string",
            "enum": [
              "and",
              "or"
            ],
            "description": "How the condition groups combine. Defaults to `and`."
          },
          "condition_groups": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "conditions"
              ],
              "properties": {
                "logic": {
                  "type": "string",
                  "enum": [
                    "and",
                    "or"
                  ],
                  "description": "How the conditions inside this group combine. Defaults to `and`."
                },
                "conditions": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CascadeCondition"
                  }
                }
              }
            }
          }
        }
      },
      "CascadeCondition": {
        "type": "object",
        "description": "A single comparison against a product node's output (or a built-in context variable such as `applicant.email`).",
        "required": [
          "source_node_id",
          "field",
          "operator"
        ],
        "properties": {
          "source_node_id": {
            "type": "string",
            "description": "The `id` of the product node whose output is compared.",
            "example": "step_credit"
          },
          "field": {
            "type": "string",
            "description": "Exact output field path from that product's schema, e.g. `creditScore.fico9`.",
            "example": "creditScore.fico9"
          },
          "operator": {
            "type": "string",
            "enum": [
              "equals",
              "not_equals",
              "greater_than",
              "greater_than_or_equal",
              "less_than",
              "less_than_or_equal",
              "contains",
              "not_contains",
              "starts_with",
              "ends_with",
              "is_empty",
              "is_not_empty",
              "is_true",
              "is_false"
            ]
          },
          "value": {
            "description": "The comparison value. Required for every operator except the unary ones (`is_empty`, `is_not_empty`, `is_true`, `is_false`), which accept `null` or no `value` at all.",
            "oneOf": [
              {
                "type": "string",
                "nullable": true
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              }
            ],
            "example": 650
          }
        }
      },
      "Workflow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 27
          },
          "object": {
            "type": "string",
            "example": "workflow"
          },
          "code": {
            "type": "string",
            "nullable": true,
            "example": "TSS-27",
            "description": "Short human-readable code assigned by Cascades."
          },
          "name": {
            "type": "string",
            "example": "Tenant Screening Standard"
          },
          "active": {
            "type": "boolean",
            "description": "`true` = activated and triggerable in live mode. Drafts (`false`) can still be triggered in test mode."
          },
          "trigger_type": {
            "type": "string",
            "enum": [
              "manual",
              "event",
              "scheduled"
            ],
            "example": "manual"
          },
          "use_case": {
            "type": "string",
            "example": "tenant_screening",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "active",
          "trigger_type"
        ]
      },
      "WorkflowDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Workflow"
          },
          {
            "type": "object",
            "properties": {
              "definition": {
                "$ref": "#/components/schemas/CascadeDefinition"
              }
            }
          }
        ]
      },
      "WorkflowCreateRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "example": "Tenant Screening Standard"
          },
          "definition": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CascadeDefinition"
              }
            ],
            "description": "The node graph. Omit or pass `[]` to create an empty draft you fill in later."
          },
          "use_case": {
            "type": "string",
            "example": "tenant_screening",
            "description": "Optional use case label."
          },
          "trigger_type": {
            "type": "string",
            "enum": [
              "manual",
              "event",
              "scheduled"
            ],
            "default": "manual",
            "description": "API-created cascades default to `manual` so they can be triggered through this API. Unknown values fall back to `manual`."
          },
          "active": {
            "type": "boolean",
            "default": true,
            "description": "Whether the cascade is immediately triggerable in live mode.\nOmitted or `null` means `true`; pass `false` for a draft. Accounts\nwithout production access always receive a draft regardless of\nthis value, and the response carries a `note` explaining why.\n"
          }
        }
      },
      "WorkflowUpdateRequest": {
        "type": "object",
        "description": "PATCH semantics — only the keys present in the body change; omitted keys keep their current value.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "definition": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CascadeDefinition"
              }
            ],
            "description": "Replacement node graph (the full array, not a diff). Read the current one with `getWorkflow` first."
          },
          "use_case": {
            "type": "string",
            "nullable": true
          },
          "trigger_type": {
            "type": "string",
            "enum": [
              "manual",
              "event",
              "scheduled"
            ],
            "description": "Unknown values are ignored (the current value is kept)."
          },
          "active": {
            "type": "boolean",
            "description": "`true` activates, `false` makes it a draft. Activating requires production access (`403 activation_forbidden` otherwise)."
          }
        }
      },
      "WorkflowGenerateRequest": {
        "type": "object",
        "required": [
          "prompt"
        ],
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000,
            "description": "Plain-English description of the decision policy to turn into a cascade (max 5 000 characters by default).",
            "example": "Pull an Equifax credit report. Approve if FICO is at least 650; send an adverse-action notice otherwise."
          },
          "name": {
            "type": "string",
            "description": "Optional name override; otherwise the generator names the cascade."
          },
          "use_case": {
            "type": "string",
            "description": "Optional use case label. Defaults to `tenant_screening`."
          },
          "create": {
            "type": "boolean",
            "default": true,
            "description": "`true` (default) saves the generated cascade and returns `201`; `false` returns the generated definition as a `200` preview without saving."
          }
        }
      },
      "CascadeGeneration": {
        "type": "object",
        "description": "Metadata about the AI generation that produced a definition.",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name the generator chose."
          },
          "model": {
            "type": "string",
            "example": "claude-opus-4-8",
            "description": "The model that produced the definition."
          },
          "repaired": {
            "type": "boolean",
            "description": "`true` when the first draft failed validation and a repair pass fixed it."
          },
          "warnings": {
            "type": "array",
            "description": "Non-blocking findings from the definition validator (unreachable nodes, compliance hints).",
            "items": {
              "$ref": "#/components/schemas/CascadeGenerationWarning"
            }
          }
        }
      },
      "CascadeGenerationWarning": {
        "type": "object",
        "description": "One non-blocking finding from the definition validator, addressed to the node it concerns. The cascade is still valid and runnable.",
        "required": [
          "node_id",
          "code",
          "message"
        ],
        "properties": {
          "node_id": {
            "type": "string",
            "example": "step_fico_check",
            "description": "The `id` of the node the finding is about."
          },
          "code": {
            "type": "string",
            "example": "missing_adverse_action",
            "description": "Machine-readable finding. Current codes: `unreachable_node`, `missing_adverse_action`, `generic_adverse_action_reason`, `folded_thresholds`, `jurisdiction_review`."
          },
          "message": {
            "type": "string",
            "example": "Decision node \"step_fico_check\" has a fail branch that does not end in an AdverseAction. FCRA declines should send an adverse-action notice.",
            "description": "Human-readable explanation with the suggested fix."
          }
        }
      },
      "CascadeGenerationPreview": {
        "type": "object",
        "description": "Returned by `generateWorkflow` when `create` is `false`. Nothing is saved.",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "cascade_generation"
            ]
          },
          "definition": {
            "$ref": "#/components/schemas/CascadeDefinition"
          },
          "valid": {
            "type": "boolean",
            "description": "Whether the definition passed the validator."
          },
          "name": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "repaired": {
            "type": "boolean"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CascadeGenerationWarning"
            }
          }
        }
      },
      "SandboxDraftNote": {
        "type": "object",
        "properties": {
          "note": {
            "type": "string",
            "description": "Present only when the account lacks production access and the cascade was therefore created as a draft.",
            "example": "Created as a draft: activating a cascade requires production access. Drafts run in test mode; live runs need an activated account."
          }
        }
      },
      "TestCase": {
        "type": "object",
        "description": "A registered sandbox subject for one of the cascade's products, with a payload ready to send to `triggerWorkflow`.",
        "properties": {
          "id": {
            "type": "integer",
            "example": 54,
            "description": "The subject id in the CRS Testcase registry."
          },
          "case_name": {
            "type": "string",
            "example": "Cascades_Credit_660_Inq0"
          },
          "product_code": {
            "type": "string",
            "example": "TUC1001",
            "description": "The billing product code this subject was registered for."
          },
          "payload": {
            "type": "object",
            "description": "Drop-in body for `triggerWorkflow` (`payload.applicant` and, when the subject carries one, `payload.property`).",
            "properties": {
              "applicant": {
                "$ref": "#/components/schemas/ApplicantPayload"
              },
              "property": {
                "type": "object",
                "additionalProperties": true,
                "description": "Property / tenancy details when the subject was registered with them."
              }
            }
          }
        }
      },
      "TestCaseList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "test_case_list"
            ]
          },
          "workflow_id": {
            "type": "integer",
            "example": 27
          },
          "test_cases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TestCase"
            }
          }
        }
      },
      "WorkflowTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "object": {
            "type": "string",
            "example": "workflow_template"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "icon": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "active": {
            "type": "boolean"
          },
          "featured": {
            "type": "boolean"
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "WorkflowTemplateDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WorkflowTemplate"
          },
          {
            "type": "object",
            "properties": {
              "definition": {
                "$ref": "#/components/schemas/CascadeDefinition"
              },
              "source_workflow_id": {
                "type": "integer",
                "nullable": true
              }
            }
          }
        ]
      },
      "ExecutionStatus": {
        "type": "string",
        "description": "Lifecycle of an execution. `queued` (batch rows not yet dispatched),\n`pending`, `running` and `waiting` (paused on a Wait node or\nadverse-action waiting period) are in flight; `awaiting_review` is\npaused on human review; `completed`, `failed`, `cancelled` and\n`skipped` are terminal.\n",
        "enum": [
          "queued",
          "pending",
          "running",
          "waiting",
          "awaiting_review",
          "completed",
          "failed",
          "cancelled",
          "skipped"
        ]
      },
      "ReviewStatus": {
        "type": "string",
        "enum": [
          "none",
          "in_review",
          "approved",
          "denied"
        ]
      },
      "ExecutionSummary": {
        "type": "object",
        "description": "The shape returned by `triggerWorkflow`. Compared with `Execution`\nit carries `execution_mode` and never `assigned_to_user_id`.\n",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1842
          },
          "object": {
            "type": "string",
            "example": "execution"
          },
          "workflow_id": {
            "type": "integer",
            "example": 27
          },
          "status": {
            "$ref": "#/components/schemas/ExecutionStatus"
          },
          "execution_mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "execution_key": {
            "type": "string",
            "nullable": true,
            "description": "The namespaced idempotency key (`api_client:<id>:<Idempotency-Key>`).",
            "example": "api_client:12:3fa85f64-5717-4562-b3fc-2c963f66afa6"
          },
          "external_reference": {
            "type": "string",
            "nullable": true,
            "example": "rv-app-20491"
          },
          "needs_human_review": {
            "type": "boolean"
          },
          "review_status": {
            "$ref": "#/components/schemas/ReviewStatus"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "id",
          "object",
          "workflow_id",
          "status",
          "needs_human_review",
          "review_status",
          "created_at"
        ]
      },
      "Execution": {
        "type": "object",
        "description": "The list/detail shape returned by the `/executions` endpoints.",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1842
          },
          "object": {
            "type": "string",
            "example": "execution"
          },
          "workflow_id": {
            "type": "integer",
            "example": 27
          },
          "status": {
            "$ref": "#/components/schemas/ExecutionStatus"
          },
          "execution_key": {
            "type": "string",
            "nullable": true,
            "example": "api_client:12:3fa85f64-5717-4562-b3fc-2c963f66afa6"
          },
          "external_reference": {
            "type": "string",
            "nullable": true,
            "example": "rv-app-20491"
          },
          "needs_human_review": {
            "type": "boolean"
          },
          "review_status": {
            "$ref": "#/components/schemas/ReviewStatus"
          },
          "assigned_to_user_id": {
            "type": "integer",
            "nullable": true,
            "description": "The reviewer who claimed this execution, if any."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "id",
          "object",
          "workflow_id",
          "status",
          "needs_human_review",
          "review_status",
          "created_at"
        ]
      },
      "ExecutionDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Execution"
          },
          {
            "type": "object",
            "properties": {
              "input_context": {
                "type": "object",
                "additionalProperties": true,
                "description": "The payload originally sent at trigger time, plus resolved\nfields such as `execution_mode`, `submitted_by_user_id`,\n`user_approved`, `external_reference`, (for a test-mode\nrun of a draft) `allow_inactive` and (for a test-mode run\nthat replays a registered test subject) `sandbox_replay`.\n"
              }
            }
          }
        ]
      },
      "ExecutionFull": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ExecutionDetail"
          },
          {
            "type": "object",
            "description": "Adds raw step output and failure detail. Gated behind `executions:read:full` because step_outputs typically contains applicant SSN, DOB, addresses, and vendor responses.",
            "properties": {
              "step_outputs": {
                "type": "object",
                "description": "Per-node results keyed by node id.",
                "additionalProperties": {
                  "type": "object"
                }
              },
              "failed_steps": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        ]
      },
      "ReviewEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "object": {
            "type": "string",
            "example": "review_event"
          },
          "execution_id": {
            "type": "integer"
          },
          "event_type": {
            "type": "string",
            "enum": [
              "flagged",
              "assigned",
              "unassigned",
              "approved",
              "denied",
              "note_added",
              "re_evaluated",
              "adverse_action_drafted",
              "adverse_action_sent",
              "adverse_action_skipped",
              "adverse_action_replayed"
            ],
            "description": "The review events plus the adverse-action notice trail:\n`adverse_action_drafted` (a notice draft was parked for review),\n`adverse_action_sent` (the draft was sent), `adverse_action_skipped`\n(the draft was discarded without sending) and\n`adverse_action_replayed` (a sent notice was resent from the app).\n"
          },
          "actor_user_id": {
            "type": "integer",
            "nullable": true
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Artifact": {
        "type": "object",
        "description": "Metadata for a downloadable PDF attached to an execution.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Artifact id; pass it as `artifact_id` to the matching download endpoint."
          },
          "object": {
            "type": "string",
            "enum": [
              "report_artifact",
              "notice_artifact"
            ]
          },
          "execution_id": {
            "type": "integer"
          },
          "filename": {
            "type": "string",
            "example": "Adverse_Action_Notice.pdf"
          },
          "content_type": {
            "type": "string",
            "example": "application/pdf"
          },
          "byte_size": {
            "type": "integer"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "downloadable": {
            "type": "boolean",
            "description": "`false` when the record exists but its file is no longer stored. Downloading it then returns `404 not_found`."
          }
        }
      },
      "ApplicantPayload": {
        "type": "object",
        "description": "Free-form applicant data forwarded to the workflow's first node as `context.applicant`. Field names depend on the workflow definition.",
        "additionalProperties": true,
        "example": {
          "first_name": "Jordan",
          "last_name": "Lee",
          "email": "jordan@example.com",
          "ssn": "123-45-6789",
          "dob": "1989-04-12"
        }
      },
      "TriggerRequest": {
        "type": "object",
        "description": "All fields are optional; an empty body starts a test-mode run with an empty payload.",
        "properties": {
          "payload": {
            "type": "object",
            "description": "The decision input. Put the subject under `applicant`; arbitrary nested fields are allowed and become the execution's `input_context`. Defaults to `{}` when omitted.",
            "properties": {
              "applicant": {
                "$ref": "#/components/schemas/ApplicantPayload"
              },
              "property": {
                "type": "object",
                "additionalProperties": true,
                "description": "Optional property / tenancy details (as returned by `listWorkflowTestCases`)."
              }
            },
            "additionalProperties": true
          },
          "execution_mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ],
            "default": "test",
            "description": "`test` uses synthetic vendor data and never bills; `live` requires production access and an active cascade."
          },
          "external_reference": {
            "type": "string",
            "maxLength": 200,
            "example": "rv-app-20491",
            "description": "Your correlation id for this run. Echoed on the execution and on every webhook event."
          }
        }
      },
      "TriggerResponse": {
        "type": "object",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/ExecutionSummary"
          },
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        }
      },
      "ReviewDecisionRequest": {
        "type": "object",
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "approved",
              "denied"
            ]
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "decision"
        ]
      },
      "ReviewAssignmentRequest": {
        "type": "object",
        "properties": {
          "assignee": {
            "type": "string",
            "nullable": true,
            "enum": [
              "me",
              null
            ],
            "description": "`me` claims for the calling api_user; null unassigns."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "ReviewNoteRequest": {
        "type": "object",
        "properties": {
          "note": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "note"
        ]
      },
      "BulkReviewRequest": {
        "type": "object",
        "properties": {
          "execution_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "minItems": 1,
            "maxItems": 25
          },
          "bulk_action": {
            "type": "string",
            "enum": [
              "approve",
              "deny",
              "assign_to_me"
            ]
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "execution_ids",
          "bulk_action"
        ]
      },
      "BulkReviewResult": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "example": "bulk_review_result"
          },
          "applied": {
            "type": "integer"
          },
          "enqueued": {
            "type": "integer"
          },
          "skipped": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "reason": {
                  "type": "string",
                  "enum": [
                    "not_awaiting_review",
                    "review_not_pending",
                    "assigned_elsewhere",
                    "live_mode_forbidden",
                    "already_assigned",
                    "not_found"
                  ]
                }
              }
            }
          }
        }
      },
      "Batch": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 77
          },
          "object": {
            "type": "string",
            "example": "batch"
          },
          "workflow_id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "interrupted",
              "completed",
              "cancelled",
              "failed"
            ],
            "description": "`completed` means every row reached a terminal status, even when\nsome rows failed (see `failed_count`). `failed` means the batch\nitself stopped on an unexpected error; it is not accepted by\n`filter[status]` on `listBatches`.\n"
          },
          "execution_mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "external_reference": {
            "type": "string",
            "nullable": true,
            "example": "rentvine-batch-001"
          },
          "total_count": {
            "type": "integer"
          },
          "completed_count": {
            "type": "integer"
          },
          "failed_count": {
            "type": "integer"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BatchExecutionRow": {
        "type": "object",
        "description": "Subset of Execution fields surfaced when listing the children of a\nbatch. Tokens with `executions:read:full` additionally receive\n`applicant_name` and `applicant_email`.\n",
        "properties": {
          "id": {
            "type": "integer"
          },
          "object": {
            "type": "string",
            "example": "execution"
          },
          "batch_execution_id": {
            "type": "integer"
          },
          "workflow_id": {
            "type": "integer"
          },
          "row_index": {
            "type": "integer",
            "description": "0-based position in the submitted `rows` array."
          },
          "status": {
            "$ref": "#/components/schemas/ExecutionStatus"
          },
          "retry_count": {
            "type": "integer"
          },
          "external_reference": {
            "type": "string",
            "nullable": true
          },
          "needs_human_review": {
            "type": "boolean"
          },
          "review_status": {
            "$ref": "#/components/schemas/ReviewStatus"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "applicant_name": {
            "type": "string",
            "nullable": true,
            "description": "Only with `executions:read:full`."
          },
          "applicant_email": {
            "type": "string",
            "nullable": true,
            "description": "Only with `executions:read:full`."
          }
        }
      },
      "BatchCreateRequest": {
        "type": "object",
        "properties": {
          "rows": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "description": "One object per applicant. Every row must be an object.",
            "items": {
              "type": "object",
              "properties": {
                "external_reference": {
                  "type": "string",
                  "maxLength": 200
                },
                "applicant": {
                  "$ref": "#/components/schemas/ApplicantPayload"
                }
              }
            }
          },
          "execution_mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ],
            "default": "test"
          },
          "external_reference": {
            "type": "string",
            "maxLength": 200
          }
        },
        "required": [
          "rows"
        ]
      },
      "WebhookEventName": {
        "type": "string",
        "description": "Lifecycle events Cascades can deliver. See `x-webhooks` for each payload.",
        "enum": [
          "execution.completed",
          "execution.failed",
          "adverse_action.triggered",
          "adverse_action.completed",
          "adverse_action.failed",
          "adverse_action.retry_scheduled"
        ]
      },
      "WebhookEnvelope": {
        "type": "object",
        "description": "The body of every webhook delivery. `data` is event-specific — see\n`x-webhooks`. Nil-valued fields are omitted from `data` rather than\nsent as `null`.\n",
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Event id. Identical across every subscriber's copy of the same event and across retries; use it to de-duplicate.",
            "example": "6d1f1a5e-4a2b-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "event": {
            "$ref": "#/components/schemas/WebhookEventName"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the event was emitted (not when this attempt was sent)."
          },
          "data": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "object": {
            "type": "string",
            "example": "webhook_subscription"
          },
          "url": {
            "type": "string",
            "example": "https://hooks.partner.example/cascades"
          },
          "event_filter": {
            "type": "array",
            "description": "Empty = subscribed to ALL events.",
            "items": {
              "$ref": "#/components/schemas/WebhookEventName"
            }
          },
          "active": {
            "type": "boolean"
          },
          "last_delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookSubscriptionWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookSubscription"
          },
          {
            "type": "object",
            "description": "Returned ONLY by POST and rotate_secret. Subsequent reads (show / list / update) never re-expose the secret.",
            "properties": {
              "signing_secret": {
                "type": "string",
                "example": "whsec_4f1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9"
              }
            }
          }
        ]
      },
      "WebhookSubscriptionCreateRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint. Must resolve to a public address (no private, loopback or cloud-metadata ranges)."
          },
          "event_filter": {
            "type": "array",
            "description": "Events to receive. Omit or pass `[]` for all events; unknown names return `422`.",
            "items": {
              "$ref": "#/components/schemas/WebhookEventName"
            }
          },
          "active": {
            "type": "boolean",
            "default": true
          }
        },
        "required": [
          "url"
        ]
      },
      "WebhookSubscriptionUpdateRequest": {
        "type": "object",
        "description": "PATCH semantics — only the keys present change.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "event_filter": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventName"
            }
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "object": {
            "type": "string",
            "example": "webhook_delivery"
          },
          "webhook_subscription_id": {
            "type": "integer"
          },
          "event_type": {
            "$ref": "#/components/schemas/WebhookEventName"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "retrying",
              "succeeded",
              "failed",
              "dead"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "max_attempts": {
            "type": "integer",
            "example": 6
          },
          "last_response_status": {
            "type": "integer",
            "nullable": true
          },
          "last_attempted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "next_retry_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, expired or revoked bearer token. The `WWW-Authenticate` header carries the RFC 6750 challenge.",
        "headers": {
          "WWW-Authenticate": {
            "description": "`Bearer realm=\"api\"` when no token was sent; `Bearer error=\"invalid_token\"` when a bad one was.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "unauthorized",
                "message": "A valid bearer token is required.",
                "details": [],
                "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/unauthorized"
              }
            }
          }
        }
      },
      "InsufficientScope": {
        "description": "Token does not hold the required scope. `details` lists the scope(s) that were checked.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "insufficient_scope",
                "message": "This token does not have the required scope.",
                "details": [
                  {
                    "field": "scope",
                    "message": "executions:write"
                  }
                ],
                "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/insufficient_scope"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource does not exist or belongs to another tenant / api_client",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "not_found",
                "message": "The requested resource was not found.",
                "details": [],
                "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/not_found"
              }
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "Request body or params failed validation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "validation_failed",
                "message": "The request could not be processed.",
                "details": [
                  {
                    "field": "execution_mode",
                    "message": "must be test or live"
                  }
                ],
                "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "Resource is in a state that conflicts with the requested operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "workflow_inactive",
                "message": "Draft workflows run in test mode only. Activate the workflow before triggering it live.",
                "details": [],
                "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/workflow_inactive"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded (60 req/min per IP on `/api/v1`, 600 req/hour\nper token prefix, or 300 req/5 min per IP globally). The body is the\nstandard error envelope and `Retry-After` gives the seconds until\nthe window resets.\n",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "example": 37
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "code": "rate_limited",
                "message": "Rate limit exceeded. Retry later.",
                "details": [],
                "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/rate_limited"
              }
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "workflows",
      "description": "Cascades — the decision workflows you author (by hand, from a\ntemplate, or with the AI generator) and run against applicants.\nWorkflows are owned by the account behind the token; drafts run in\ntest mode, activated cascades can also run live.\n"
    },
    {
      "name": "workflow_templates",
      "description": "Read-only catalog of published cascade templates you can copy a `definition` from."
    },
    {
      "name": "executions",
      "description": "Individual runs of a cascade: polling status and results, cancelling,\nre-evaluating, human-review decisions and audit trail, and the PDF\nartifacts (credit reports, adverse-action notices) a run produced.\n"
    },
    {
      "name": "batches",
      "description": "Bulk runs of up to 500 applicants against one cascade, with per-row status, cancel / resume / retry, and CSV / ZIP exports."
    },
    {
      "name": "webhooks",
      "description": "Subscriptions to lifecycle events, signing-secret rotation, and the delivery log with manual retry. The events themselves are documented under `x-webhooks`."
    }
  ],
  "x-webhooks": {
    "execution.completed": {
      "post": {
        "operationId": "webhookExecutionCompleted",
        "summary": "An execution reached `completed`",
        "description": "Sent once when an execution's status transitions to `completed`.\nEmitted for API-triggered runs only (executions started from the\nCascades UI have no api_client and produce no webhooks). Poll\n`GET /executions/{workflow_execution_id}` for the decision and\n`step_outputs`.\n",
        "parameters": [
          {
            "name": "X-Cascades-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "execution.completed"
              ]
            }
          },
          {
            "name": "X-Cascades-Delivery",
            "in": "header",
            "required": true,
            "description": "The `WebhookDelivery` id.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Cascades-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix>,v1=<hmac>[,v0=<hmac>]` — HMAC-SHA256 over `\"<t>.<body>\"`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "execution.completed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "workflow_execution_id",
                      "workflow_id",
                      "status",
                      "review_status"
                    ],
                    "properties": {
                      "workflow_execution_id": {
                        "type": "integer",
                        "example": 1842
                      },
                      "workflow_id": {
                        "type": "integer",
                        "example": 27
                      },
                      "batch_execution_id": {
                        "type": "integer",
                        "description": "Present only for batch rows."
                      },
                      "external_reference": {
                        "type": "string",
                        "description": "Present when the run was triggered with one."
                      },
                      "execution_mode": {
                        "type": "string",
                        "enum": [
                          "test",
                          "live"
                        ]
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "completed"
                        ]
                      },
                      "review_status": {
                        "type": "string",
                        "enum": [
                          "none",
                          "in_review",
                          "approved",
                          "denied"
                        ]
                      },
                      "completed_at": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "6d1f1a5e-4a2b-4c7d-9e8f-0a1b2c3d4e5f",
                "event": "execution.completed",
                "created_at": "2026-09-09T14:03:21Z",
                "data": {
                  "workflow_execution_id": 1842,
                  "workflow_id": 27,
                  "external_reference": "rv-app-20491",
                  "execution_mode": "test",
                  "status": "completed",
                  "review_status": "none",
                  "completed_at": "2026-09-09T14:03:20Z"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged. Any 2xx marks the delivery `succeeded`; anything else is retried on the documented schedule."
          }
        }
      }
    },
    "execution.failed": {
      "post": {
        "operationId": "webhookExecutionFailed",
        "summary": "An execution reached `failed`",
        "description": "Sent once when an execution's status transitions to `failed`.\n`first_error_message` is the engine's first recorded error; the\nfull `failed_steps` array is available from\n`GET /executions/{workflow_execution_id}` with `executions:read:full`.\n",
        "parameters": [
          {
            "name": "X-Cascades-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "execution.failed"
              ]
            }
          },
          {
            "name": "X-Cascades-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Cascades-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "execution.failed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "workflow_execution_id",
                      "workflow_id",
                      "status",
                      "review_status"
                    ],
                    "properties": {
                      "workflow_execution_id": {
                        "type": "integer"
                      },
                      "workflow_id": {
                        "type": "integer"
                      },
                      "batch_execution_id": {
                        "type": "integer",
                        "description": "Present only for batch rows."
                      },
                      "external_reference": {
                        "type": "string"
                      },
                      "execution_mode": {
                        "type": "string",
                        "enum": [
                          "test",
                          "live"
                        ]
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "failed"
                        ]
                      },
                      "review_status": {
                        "type": "string",
                        "enum": [
                          "none",
                          "in_review",
                          "approved",
                          "denied"
                        ]
                      },
                      "completed_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "first_error_message": {
                        "type": "string",
                        "description": "The first error the engine recorded for this run."
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "9b2e7c10-3d4f-4a5b-8c6d-7e8f9a0b1c2d",
                "event": "execution.failed",
                "created_at": "2026-09-09T14:05:02Z",
                "data": {
                  "workflow_execution_id": 1843,
                  "workflow_id": 27,
                  "external_reference": "rv-app-20492",
                  "execution_mode": "live",
                  "status": "failed",
                  "review_status": "none",
                  "completed_at": "2026-09-09T14:05:01Z",
                  "first_error_message": "Equifax: subject not found"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "adverse_action.triggered": {
      "post": {
        "operationId": "webhookAdverseActionTriggered",
        "summary": "An adverse-action notice was sent (or the pre-adverse waiting period started)",
        "description": "Emitted by the `AdverseAction` node when a notice goes out. For the\ntwo-step employment flow (`notice_stage: pre_adverse`) the execution\nthen enters `waiting` until `resume_at`, when the post-adverse notice\nis sent. For single-notice flows (`notice_stage: final`) it is\nimmediately followed by `adverse_action.completed`.\n",
        "parameters": [
          {
            "name": "X-Cascades-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "adverse_action.triggered"
              ]
            }
          },
          {
            "name": "X-Cascades-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Cascades-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "adverse_action.triggered"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "workflow_execution_id",
                      "workflow_id",
                      "node_id",
                      "flow",
                      "notice_stage",
                      "status"
                    ],
                    "properties": {
                      "workflow_execution_id": {
                        "type": "integer"
                      },
                      "workflow_id": {
                        "type": "integer"
                      },
                      "node_id": {
                        "type": "string",
                        "description": "The `AdverseAction` node's id in the definition."
                      },
                      "external_reference": {
                        "type": "string"
                      },
                      "flow": {
                        "type": "string",
                        "description": "`employment` for the two-step pre/post flow; otherwise the workflow's use case (e.g. `tenant_screening`).",
                        "example": "tenant_screening"
                      },
                      "notice_stage": {
                        "type": "string",
                        "enum": [
                          "pre_adverse",
                          "final"
                        ]
                      },
                      "sent_to": {
                        "type": "string",
                        "description": "Recipient address the notice was sent to."
                      },
                      "delivery_channels": {
                        "type": "array",
                        "description": "`final` stage only: the channels used. Currently always `digital` (physical mail is not yet enabled).",
                        "items": {
                          "type": "string"
                        }
                      },
                      "triggered_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "resume_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "`pre_adverse` only: when the post-adverse notice will be sent."
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "waiting",
                          "completed"
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "1c9d8e7f-6a5b-4c3d-2e1f-0a9b8c7d6e5f",
                "event": "adverse_action.triggered",
                "created_at": "2026-09-09T14:06:40Z",
                "data": {
                  "workflow_execution_id": 1844,
                  "workflow_id": 27,
                  "node_id": "step_adverse_action",
                  "external_reference": "rv-app-20493",
                  "flow": "tenant_screening",
                  "notice_stage": "final",
                  "sent_to": "jordan@example.com",
                  "delivery_channels": [
                    "digital"
                  ],
                  "triggered_at": "2026-09-09T14:06:39Z",
                  "status": "completed"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "adverse_action.completed": {
      "post": {
        "operationId": "webhookAdverseActionCompleted",
        "summary": "The adverse-action flow finished",
        "description": "Emitted when the final (or post-adverse) notice has been delivered.\nThe notice PDF is listed by `GET /executions/{workflow_execution_id}/notice_artifacts`.\n",
        "parameters": [
          {
            "name": "X-Cascades-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "adverse_action.completed"
              ]
            }
          },
          {
            "name": "X-Cascades-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Cascades-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "adverse_action.completed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "workflow_execution_id",
                      "workflow_id",
                      "node_id",
                      "flow",
                      "notice_stage",
                      "status"
                    ],
                    "properties": {
                      "workflow_execution_id": {
                        "type": "integer"
                      },
                      "workflow_id": {
                        "type": "integer"
                      },
                      "node_id": {
                        "type": "string"
                      },
                      "external_reference": {
                        "type": "string"
                      },
                      "flow": {
                        "type": "string",
                        "example": "tenant_screening"
                      },
                      "notice_stage": {
                        "type": "string",
                        "enum": [
                          "post_adverse",
                          "final"
                        ]
                      },
                      "sent_to": {
                        "type": "string"
                      },
                      "delivery_channels": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "completed_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "completed"
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "2d0e9f8a-7b6c-4d5e-3f2a-1b0c9d8e7f6a",
                "event": "adverse_action.completed",
                "created_at": "2026-09-09T14:06:41Z",
                "data": {
                  "workflow_execution_id": 1844,
                  "workflow_id": 27,
                  "node_id": "step_adverse_action",
                  "external_reference": "rv-app-20493",
                  "flow": "tenant_screening",
                  "notice_stage": "final",
                  "sent_to": "jordan@example.com",
                  "delivery_channels": [
                    "digital"
                  ],
                  "completed_at": "2026-09-09T14:06:39Z",
                  "status": "completed"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "adverse_action.failed": {
      "post": {
        "operationId": "webhookAdverseActionFailed",
        "summary": "A notice could not be delivered",
        "description": "Emitted when sending a notice raised an error. For the post-adverse\nstage this fires only after the retry budget is exhausted (see\n`adverse_action.retry_scheduled` for the intermediate attempts); the\nexecution then fails.\n",
        "parameters": [
          {
            "name": "X-Cascades-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "adverse_action.failed"
              ]
            }
          },
          {
            "name": "X-Cascades-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Cascades-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "adverse_action.failed"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "workflow_execution_id",
                      "workflow_id",
                      "node_id",
                      "flow",
                      "notice_stage",
                      "error",
                      "status"
                    ],
                    "properties": {
                      "workflow_execution_id": {
                        "type": "integer"
                      },
                      "workflow_id": {
                        "type": "integer"
                      },
                      "node_id": {
                        "type": "string"
                      },
                      "external_reference": {
                        "type": "string"
                      },
                      "flow": {
                        "type": "string"
                      },
                      "notice_stage": {
                        "type": "string",
                        "enum": [
                          "pre_adverse",
                          "post_adverse",
                          "final"
                        ]
                      },
                      "error": {
                        "type": "string",
                        "description": "Human-readable delivery error."
                      },
                      "failed_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "attempt": {
                        "type": "integer",
                        "description": "`post_adverse` only: the attempt number that exhausted the retry budget."
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "failed"
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "3e1f0a9b-8c7d-4e6f-5a4b-2c1d0e9f8a7b",
                "event": "adverse_action.failed",
                "created_at": "2026-09-09T14:08:12Z",
                "data": {
                  "workflow_execution_id": 1845,
                  "workflow_id": 27,
                  "node_id": "step_adverse_action",
                  "flow": "tenant_screening",
                  "notice_stage": "final",
                  "error": "Failed to send adverse action notice to jordan@example.com: SMTP 550 mailbox unavailable",
                  "failed_at": "2026-09-09T14:08:11Z",
                  "status": "failed"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "adverse_action.retry_scheduled": {
      "post": {
        "operationId": "webhookAdverseActionRetryScheduled",
        "summary": "A post-adverse notice delivery failed and a retry was scheduled",
        "description": "Employment (two-step) flow only. The post-adverse notice could not\nbe delivered; Cascades scheduled another attempt at `retry_at` with\nexponential backoff. `adverse_action.failed` follows if the retry\nbudget runs out.\n",
        "parameters": [
          {
            "name": "X-Cascades-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "adverse_action.retry_scheduled"
              ]
            }
          },
          {
            "name": "X-Cascades-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Cascades-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "adverse_action.retry_scheduled"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "workflow_execution_id",
                      "workflow_id",
                      "node_id",
                      "flow",
                      "notice_stage",
                      "error",
                      "attempt",
                      "next_attempt",
                      "retry_at"
                    ],
                    "properties": {
                      "workflow_execution_id": {
                        "type": "integer"
                      },
                      "workflow_id": {
                        "type": "integer"
                      },
                      "node_id": {
                        "type": "string"
                      },
                      "external_reference": {
                        "type": "string"
                      },
                      "flow": {
                        "type": "string",
                        "enum": [
                          "employment"
                        ]
                      },
                      "notice_stage": {
                        "type": "string",
                        "enum": [
                          "post_adverse"
                        ]
                      },
                      "error": {
                        "type": "string"
                      },
                      "attempt": {
                        "type": "integer",
                        "description": "The attempt that just failed (1-based)."
                      },
                      "next_attempt": {
                        "type": "integer"
                      },
                      "retry_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "wait_id": {
                        "type": "integer",
                        "description": "Id of the internal wait record backing the retry."
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "4f2a1b0c-9d8e-4f7a-6b5c-3d2e1f0a9b8c",
                "event": "adverse_action.retry_scheduled",
                "created_at": "2026-09-09T14:09:00Z",
                "data": {
                  "workflow_execution_id": 1846,
                  "workflow_id": 31,
                  "node_id": "step_adverse_action",
                  "flow": "employment",
                  "notice_stage": "post_adverse",
                  "error": "SMTP connection timed out",
                  "attempt": 1,
                  "next_attempt": 2,
                  "retry_at": "2026-09-09T14:39:00Z",
                  "wait_id": 9021
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged."
          }
        }
      }
    }
  },
  "paths": {
    "/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "tags": [
          "workflows"
        ],
        "summary": "List workflows",
        "description": "Workflows owned by the account behind the token, newest first.\nHidden single-report runner workflows are excluded. Requires\n`workflows:read`.\n",
        "x-scopes": [
          "workflows:read"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "x-mcp": {
          "tool": "cascades_list_workflows",
          "description": "List the caller's saved Cascades (decision workflows) — their id, code, name, use_case, trigger_type, and whether each is active. Use this first when the user refers to a cascade by name or asks \"what can I run\", to discover the workflow id you then pass to cascades_trigger_workflow. Only workflows owned by the API key's account are returned. Optional filters narrow the list.",
          "annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": true
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          },
          {
            "name": "filter[active]",
            "in": "query",
            "description": "Only active (`true`) or draft (`false`) workflows.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "filter[use_case]",
            "in": "query",
            "description": "Exact match, e.g. `tenant_screening`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[trigger_type]",
            "in": "query",
            "description": "Exact match. Not validated: a value outside the list returns an empty list, not an error.",
            "schema": {
              "type": "string",
              "enum": [
                "manual",
                "event",
                "scheduled"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Workflow list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Workflow"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 27,
                      "object": "workflow",
                      "code": "TSS-27",
                      "name": "Tenant Screening Standard",
                      "active": true,
                      "trigger_type": "manual",
                      "use_case": "tenant_screening",
                      "created_at": "2026-08-01T10:00:00Z",
                      "updated_at": "2026-09-01T09:30:00Z"
                    }
                  ],
                  "meta": {
                    "page": 1,
                    "per_page": 25,
                    "total_count": 1,
                    "total_pages": 1,
                    "object": "workflow",
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createWorkflow",
        "tags": [
          "workflows"
        ],
        "summary": "Create a workflow",
        "description": "Creates a cascade from a JSON `definition` (see `CascadeDefinition`\nfor the node grammar), owned by the account behind the token.\nAPI-created cascades default to `trigger_type: manual` so they can\nbe run through `triggerWorkflow`.\n\n`active` defaults to `true`. Accounts **without production access**\nalways get a draft (`active: false`) — silently, with a `note` in\nthe response explaining why. Drafts can still be triggered in test\nmode; only live runs need an activated cascade and an approved\naccount. Requires `workflows:write`.\n",
        "x-scopes": [
          "workflows:write"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "x-mcp": {
          "tool": "cascades_create_workflow",
          "description": "Create a new cascade (decision workflow) from a JSON definition, owned by the API key's account. Use this to turn a described policy into a runnable cascade: compose the node graph in `definition` and POST it. The cascade is active by default, so you can immediately run it with cascades_trigger_workflow using the returned id — pass active:false to create a draft instead. Sandbox accounts always receive a draft, which still runs in test mode.",
          "annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false
          }
        },
        "requestBody": {
          "description": "The cascade to create: its name, use case and node definition.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowCreateRequest"
              },
              "example": {
                "name": "Tenant Screening Standard",
                "use_case": "tenant_screening",
                "trigger_type": "manual",
                "definition": [
                  {
                    "id": "step_applicant",
                    "type": "ApplicantSource",
                    "name": "Applicant",
                    "next_step": [
                      "step_credit"
                    ]
                  },
                  {
                    "id": "step_credit",
                    "type": "BillingProduct",
                    "name": "Credit report",
                    "billing_product_code": "EQF1003",
                    "next_step": [
                      "step_score_check"
                    ]
                  },
                  {
                    "id": "step_score_check",
                    "type": "IfElse",
                    "name": "Score at least 650?",
                    "branches": {
                      "pass": {
                        "condition_groups": [
                          {
                            "conditions": [
                              {
                                "source_node_id": "step_credit",
                                "field": "creditScore.fico9",
                                "operator": "greater_than_or_equal",
                                "value": 650
                              }
                            ]
                          }
                        ]
                      }
                    },
                    "pass_steps": [
                      "step_approve"
                    ],
                    "fail_steps": [
                      "step_adverse_action"
                    ]
                  },
                  {
                    "id": "step_approve",
                    "type": "Action",
                    "name": "Approve",
                    "action_type": "Approve"
                  },
                  {
                    "id": "step_adverse_action",
                    "type": "Action",
                    "name": "Adverse action",
                    "action_type": "AdverseAction",
                    "adverse_action": {
                      "recipient": "{{applicant.email}}",
                      "action_taken": "denied",
                      "send_strategy": "draft_first",
                      "reason": "Credit score below the property minimum."
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. `note` is present only when the account lacks production access and the cascade was saved as a draft.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Workflow"
                        },
                        {
                          "$ref": "#/components/schemas/SandboxDraftNote"
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "examples": {
                  "approved_account": {
                    "summary": "Account with production access",
                    "value": {
                      "data": {
                        "id": 41,
                        "object": "workflow",
                        "code": "TSS-41",
                        "name": "Tenant Screening Standard",
                        "active": true,
                        "trigger_type": "manual",
                        "use_case": "tenant_screening",
                        "created_at": "2026-09-09T14:00:00Z",
                        "updated_at": "2026-09-09T14:00:00Z"
                      },
                      "meta": {
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                      }
                    }
                  },
                  "sandbox_account": {
                    "summary": "Sandbox account (saved as a draft)",
                    "value": {
                      "data": {
                        "id": 41,
                        "object": "workflow",
                        "code": "TSS-41",
                        "name": "Tenant Screening Standard",
                        "active": false,
                        "trigger_type": "manual",
                        "use_case": "tenant_screening",
                        "created_at": "2026-09-09T14:00:00Z",
                        "updated_at": "2026-09-09T14:00:00Z",
                        "note": "Created as a draft: activating a cascade requires production access. Drafts run in test mode; live runs need an activated account."
                      },
                      "meta": {
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`malformed_body` (unparseable JSON) or `bad_request`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "malformed_body",
                    "message": "The request body could not be parsed. Send valid JSON matching the Content-Type header.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/malformed_body"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "description": "`validation_failed` — e.g. blank `name`, a `definition` that is not an array of objects, or more than one `ApplicantSource` node.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The workflow could not be created.",
                    "details": [
                      {
                        "field": "name",
                        "message": "can't be blank"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/workflows/generate": {
      "post": {
        "operationId": "generateWorkflow",
        "tags": [
          "workflows"
        ],
        "summary": "Generate a workflow from a prompt",
        "description": "Builds a cascade definition from a plain-English policy description\nusing the AI builder, validates it (with one automatic repair pass),\nand — by default — saves it as a new workflow exactly like\n`createWorkflow` (same draft rule for sandbox accounts, same `note`).\nPass `create: false` to receive the generated definition as a\npreview without saving anything.\n\nGeneration shares a small concurrency budget with the in-app\ndesigner; when every slot is busy the call returns\n`503 designer_busy` and should be retried after a short delay.\nRequires `workflows:write`.\n",
        "x-scopes": [
          "workflows:write"
        ],
        "x-doc-page": "/docs/guides/ai-generate-a-cascade",
        "x-mcp": {
          "tool": "cascades_generate_workflow",
          "description": "Build a cascade from a plain-English policy description using the Cascades AI builder, then create it (by default) so it is immediately runnable with cascades_trigger_workflow. Use this when the user describes a screening/decision policy in words rather than supplying a node graph. Returns the created cascade (with the AI-generated name and any warnings); pass create:false to preview the generated definition without saving it.",
          "annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false
          }
        },
        "requestBody": {
          "description": "A plain-language description of the screening policy to turn into a cascade.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowGenerateRequest"
              },
              "examples": {
                "create": {
                  "summary": "Generate and save",
                  "value": {
                    "prompt": "Pull an Equifax credit report. Approve if FICO is at least 650; otherwise send an adverse-action notice for review.",
                    "use_case": "tenant_screening"
                  }
                },
                "preview": {
                  "summary": "Preview only",
                  "value": {
                    "prompt": "Pull an Equifax credit report. Approve if FICO is at least 650; otherwise send an adverse-action notice for review.",
                    "create": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Generated and saved. `generation` describes the AI pass; `note` appears for sandbox accounts (draft).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Workflow"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "generation": {
                              "$ref": "#/components/schemas/CascadeGeneration"
                            }
                          }
                        },
                        {
                          "$ref": "#/components/schemas/SandboxDraftNote"
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 42,
                    "object": "workflow",
                    "code": "FCS-42",
                    "name": "FICO 650 Screening",
                    "active": true,
                    "trigger_type": "manual",
                    "use_case": "tenant_screening",
                    "created_at": "2026-09-09T14:10:00Z",
                    "updated_at": "2026-09-09T14:10:00Z",
                    "generation": {
                      "name": "FICO 650 Screening",
                      "model": "claude-opus-4-8",
                      "repaired": false,
                      "warnings": []
                    }
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "200": {
            "description": "Preview (`create: false`). Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CascadeGenerationPreview"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "description": "`validation_failed` (blank or over-long `prompt`, or the saved workflow failed validation) or `generation_failed` (the model could not produce a valid definition; `details` lists the validator's findings).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "generation_failed": {
                    "value": {
                      "error": {
                        "code": "generation_failed",
                        "message": "The generated cascade did not pass validation.",
                        "details": [
                          {
                            "field": "definition",
                            "message": "Decision node \"step_score_check\" uses operator \">=\", which is not valid."
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/generation_failed"
                      }
                    }
                  },
                  "prompt_blank": {
                    "value": {
                      "error": {
                        "code": "validation_failed",
                        "message": "A prompt is required.",
                        "details": [
                          {
                            "field": "prompt",
                            "message": "can't be blank"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "`designer_busy` — every generation slot is in use. Retry after a few seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "designer_busy",
                    "message": "The cascade designer is busy right now. Please try again in a moment.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/designer_busy"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workflows/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WorkflowIdParam"
        }
      ],
      "get": {
        "operationId": "getWorkflow",
        "tags": [
          "workflows"
        ],
        "summary": "Show a workflow",
        "description": "Returns the workflow including its full `definition`. Requires `workflows:read`.",
        "x-scopes": [
          "workflows:read"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "x-mcp": {
          "tool": "cascades_get_workflow",
          "description": "Fetch one cascade by id, including its full node graph (`definition`). Use this to read a cascade before updating it with cascades_update_workflow, or to inspect how it is built. Only cascades owned by the API key's account are returned (a foreign or deleted id 404s).",
          "annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": true
          }
        },
        "responses": {
          "200": {
            "description": "Workflow with definition",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WorkflowDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateWorkflow",
        "tags": [
          "workflows"
        ],
        "summary": "Update a workflow",
        "description": "Partial update: only the keys present in the body change. To edit the\ngraph, read it with `getWorkflow`, modify the array, and send the full\n`definition` back. `PUT /workflows/{id}` is accepted as an alias with\nidentical (partial) semantics.\n\nSetting `active: true` from an account without production access is\nrefused with `403 activation_forbidden` rather than silently\ndropped. Requires `workflows:write`.\n",
        "x-scopes": [
          "workflows:write"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "x-mcp": {
          "tool": "cascades_update_workflow",
          "description": "Update an existing cascade. PARTIAL update — only the fields you pass change; omitted fields keep their current value. To edit the node graph, first read it with cascades_get_workflow, modify the `definition`, then pass the full new array back. Requires the workflows:write scope; you can only update cascades owned by the API key's account.",
          "annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": true
          }
        },
        "requestBody": {
          "description": "The fields to change. Omitted fields keep their current value.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowUpdateRequest"
              },
              "examples": {
                "rename": {
                  "summary": "Rename only",
                  "value": {
                    "name": "Tenant Screening — Premium"
                  }
                },
                "deactivate": {
                  "summary": "Make it a draft",
                  "value": {
                    "active": false
                  }
                },
                "replace_definition": {
                  "summary": "Replace the node graph",
                  "value": {
                    "definition": [
                      {
                        "id": "step_applicant",
                        "type": "ApplicantSource",
                        "name": "Applicant",
                        "next_step": [
                          "step_approve"
                        ]
                      },
                      {
                        "id": "step_approve",
                        "type": "Action",
                        "name": "Approve",
                        "action_type": "Approve"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated workflow with definition",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WorkflowDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`malformed_body`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope` or `activation_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "activation_forbidden": {
                    "value": {
                      "error": {
                        "code": "activation_forbidden",
                        "message": "Activating a cascade requires production access. It stays a draft, which runs in test mode.",
                        "details": [
                          {
                            "field": "active",
                            "message": "requires production access"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/activation_forbidden"
                      }
                    }
                  },
                  "insufficient_scope": {
                    "value": {
                      "error": {
                        "code": "insufficient_scope",
                        "message": "This token does not have the required scope.",
                        "details": [
                          {
                            "field": "scope",
                            "message": "workflows:write"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/insufficient_scope"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "`validation_failed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The workflow could not be updated.",
                    "details": [
                      {
                        "field": "definition",
                        "message": "can only have one Applicant Source node"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteWorkflow",
        "tags": [
          "workflows"
        ],
        "summary": "Delete a workflow",
        "description": "Soft-deletes the cascade. It disappears from `listWorkflows`, can no\nlonger be triggered, and its id returns `404` from then on. Existing\nexecutions and their artifacts are retained. Requires `workflows:write`.\n",
        "x-scopes": [
          "workflows:write"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "x-mcp": {
          "tool": "cascades_delete_workflow",
          "description": "Delete a cascade (soft-delete). Afterwards it no longer appears in cascades_list_workflows and cannot be triggered. Requires the workflows:write scope; only cascades owned by the API key's account can be deleted. Confirm with the user before deleting.",
          "annotations": {
            "readOnlyHint": false,
            "destructiveHint": true,
            "idempotentHint": true
          }
        },
        "responses": {
          "204": {
            "description": "Deleted (no body)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/workflows/{id}/test_cases": {
      "get": {
        "operationId": "listWorkflowTestCases",
        "tags": [
          "workflows"
        ],
        "summary": "List sandbox test cases for a workflow",
        "description": "The synthetic subjects registered in the CRS Testcase Service for\nthis cascade's product nodes (credit, criminal, eviction …). Each\ncarries a ready-to-run `payload` shaped for `triggerWorkflow`, so a\ntest-mode run needs no hand-written applicant data. A test-mode\ntrigger for a subject registered for every product node in the\ncascade replays its authored bureau responses at no charge\n(`input_context.sandbox_replay` is `true`); a subject that covers\nonly some of the products gets schema mocks like any other applicant\n(set `payload.scenario` to `fail_path` to exercise decline branches).\nLive-mode sandbox runs replay registered subjects too.\nA credit case's own criteria carry only name and SSN, so blank\nfields (`dob`, the `property` address) are filled from the same\nsubject's other registry cases, such as its criminal or eviction\ncase, whichever products the cascade uses. Without them CRS refuses\nthe credit order.\nOnly the cases authored for Cascades (`case_name` starts with\n`Cascades_`) are listed. The registry's other fixtures mostly lack the\nfirst name and address CRS needs, so their runs would fail at the\ncredit step.\nBest-effort: an unreachable registry yields an empty list, never a\n5xx. Results are cached for a few minutes. Requires `workflows:read`.\n",
        "x-scopes": [
          "workflows:read"
        ],
        "x-doc-page": "/docs/sandbox/test-identities",
        "x-mcp": {
          "tool": "cascades_list_test_cases",
          "description": "List the pre-registered sandbox test-case subjects for a cascade — the synthetic applicants the CRS Testcase Service has authored for that cascade's products (e.g. specific credit-score bands). Use this when asked to \"run the test cases\", \"try a sample applicant\", or to sanity-check a cascade without hand-writing applicant data. Each result carries a ready-to-run `payload` (applicant + property) you pass straight to cascades_trigger_workflow with execution_mode \"test\". Returns an empty list when the cascade's products have no registered subjects.",
          "annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": true
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkflowIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Test-case list (not paginated)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TestCaseList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "object": "test_case_list",
                    "workflow_id": 27,
                    "test_cases": [
                      {
                        "id": 73,
                        "case_name": "Cascades_XProduct_Approve_EFX",
                        "product_code": "EQF1003",
                        "payload": {
                          "applicant": {
                            "first_name": "AVERY",
                            "last_name": "CLARKSON",
                            "middle_name": "",
                            "name": "AVERY CLARKSON",
                            "email": "avery.clarkson@example.com",
                            "phone": "",
                            "ssn": "666900001",
                            "dob": "1982-01-01"
                          },
                          "property": {
                            "address": "1803 Norma",
                            "unit": "",
                            "city": "Cottonwood",
                            "state": "CA",
                            "zip": "91502",
                            "full_address": "1803 Norma, Cottonwood, CA 91502"
                          }
                        }
                      }
                    ]
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/workflows/{id}/trigger": {
      "post": {
        "operationId": "triggerWorkflow",
        "tags": [
          "workflows"
        ],
        "summary": "Trigger a workflow",
        "description": "Starts an execution and returns `202` immediately; the run proceeds\nasynchronously. Poll `getExecution` until `status` is terminal\n(`completed`, `failed`, `cancelled`, `skipped`) or `awaiting_review`,\nor subscribe to `execution.completed` / `execution.failed`.\n\n**Test vs live.** `execution_mode` defaults to `test`, which uses\nsynthetic vendor data and never bills — and works on drafts\n(`active: false`), so you can exercise a cascade before activating\nit. `live` requires an account with production access\n(`403 live_mode_forbidden` otherwise) **and** an active cascade\n(`409 workflow_inactive` otherwise).\n\n**Idempotency.** `Idempotency-Key` is required\n(`422 missing_idempotency_key` if absent). Race-safe under concurrent\nidentical submissions: the loser receives the same execution as the\nwinner. A reused key with a different body returns\n`409 idempotency_conflict`. Requires `executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-idempotent": true,
        "x-async": {
          "poll": "getExecution"
        },
        "x-doc-page": "/docs/concepts/executions",
        "x-mcp": {
          "tool": "cascades_trigger_workflow",
          "description": "Run a saved cascade for an applicant — i.e. start a decision. POSTs to /api/v1/workflows/:id/trigger and returns the created execution, which starts in status \"pending\" (the run is async). After calling this, poll cascades_get_execution with the returned execution id until status is terminal (completed / failed / cancelled / awaiting_review). execution_mode defaults to \"test\", which also works on draft cascades; \"live\" requires a production-approved account and an active cascade and is otherwise rejected.",
          "annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkflowIdParam"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "description": "The applicant payload and the execution mode for this run.",
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TriggerRequest"
              },
              "example": {
                "payload": {
                  "applicant": {
                    "first_name": "Jordan",
                    "last_name": "Lee",
                    "email": "jordan@example.com"
                  }
                },
                "execution_mode": "test",
                "external_reference": "rv-app-20491"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Execution accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TriggerResponse"
                },
                "example": {
                  "data": {
                    "id": 1842,
                    "object": "execution",
                    "workflow_id": 27,
                    "status": "pending",
                    "execution_mode": "test",
                    "execution_key": "api_client:12:3fa85f64-5717-4562-b3fc-2c963f66afa6",
                    "external_reference": "rv-app-20491",
                    "needs_human_review": false,
                    "review_status": "none",
                    "started_at": null,
                    "completed_at": null,
                    "created_at": "2026-09-09T14:00:00Z",
                    "updated_at": "2026-09-09T14:00:00Z"
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`malformed_body`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope` or `live_mode_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "live_mode_forbidden": {
                    "value": {
                      "error": {
                        "code": "live_mode_forbidden",
                        "message": "Live mode requires production access.",
                        "details": [
                          {
                            "field": "execution_mode",
                            "message": "live mode requires production access"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/live_mode_forbidden"
                      }
                    }
                  },
                  "insufficient_scope": {
                    "value": {
                      "error": {
                        "code": "insufficient_scope",
                        "message": "This token does not have the required scope.",
                        "details": [
                          {
                            "field": "scope",
                            "message": "executions:write"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/insufficient_scope"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`idempotency_conflict` or `workflow_inactive` (live trigger on a draft)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "idempotency_conflict": {
                    "value": {
                      "error": {
                        "code": "idempotency_conflict",
                        "message": "The Idempotency-Key has already been used with a different request payload.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/idempotency_conflict"
                      }
                    }
                  },
                  "workflow_inactive": {
                    "value": {
                      "error": {
                        "code": "workflow_inactive",
                        "message": "Draft workflows run in test mode only. Activate the workflow before triggering it live.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/workflow_inactive"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`missing_idempotency_key` or `validation_failed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "missing_idempotency_key": {
                    "value": {
                      "error": {
                        "code": "missing_idempotency_key",
                        "message": "Idempotency-Key header is required.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/missing_idempotency_key"
                      }
                    }
                  },
                  "validation_failed": {
                    "value": {
                      "error": {
                        "code": "validation_failed",
                        "message": "The request could not be processed.",
                        "details": [
                          {
                            "field": "execution_mode",
                            "message": "must be test or live"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/workflow_templates": {
      "get": {
        "operationId": "listWorkflowTemplates",
        "tags": [
          "workflow_templates"
        ],
        "summary": "List published templates",
        "description": "Read-only catalog, ordered by name. Drafts and inactive templates are never exposed via the API. Requires `workflow_templates:read`.",
        "x-scopes": [
          "workflow_templates:read"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          },
          {
            "name": "filter[featured]",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "filter[category]",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkflowTemplate"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/workflow_templates/{id}": {
      "get": {
        "operationId": "getWorkflowTemplate",
        "tags": [
          "workflow_templates"
        ],
        "summary": "Show a template (with definition)",
        "description": "Returns the template including its `definition`, which you can pass to `createWorkflow`. Requires `workflow_templates:read`.",
        "x-scopes": [
          "workflow_templates:read"
        ],
        "x-doc-page": "/docs/concepts/cascades",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WorkflowTemplateDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions": {
      "get": {
        "operationId": "listExecutions",
        "tags": [
          "executions"
        ],
        "summary": "List executions",
        "description": "Executions started by the calling api_client, newest first. CSV\nfilters (`status`, `review_status`) accept several comma-separated\nvalues; an unknown value returns `422`. Requires `executions:read`.\n",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/executions",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          },
          {
            "name": "filter[workflow_id]",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "filter[status]",
            "in": "query",
            "description": "CSV of `queued, pending, running, waiting, awaiting_review, completed, failed, cancelled, skipped`",
            "schema": {
              "type": "string"
            },
            "example": "completed,failed"
          },
          {
            "name": "filter[needs_human_review]",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "filter[review_status]",
            "in": "query",
            "description": "CSV of `none, in_review, approved, denied`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[batch_execution_id]",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "filter[execution_key]",
            "in": "query",
            "description": "Exact match on the namespaced idempotency key.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[external_reference]",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Execution list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Execution"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}": {
      "get": {
        "operationId": "getExecution",
        "tags": [
          "executions"
        ],
        "summary": "Show an execution",
        "description": "Returns the detail view (`Execution` + `input_context`). Tokens with\n`executions:read:full` additionally receive `step_outputs` and\n`failed_steps`, which typically contain applicant PII and vendor\nresponses. This is the endpoint to poll after `triggerWorkflow` and\n`reEvaluateExecution`. Requires `executions:read`.\n",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/executions",
        "x-mcp": {
          "tool": "cascades_get_execution",
          "description": "Fetch a single cascade execution by id to check its status and results. Use this to poll after cascades_trigger_workflow until the run reaches a terminal status, and to read the decision: with full read access the response includes `step_outputs` (the per-step results) and `input_context`. Statuses: queued / pending / running / waiting / awaiting_review / completed / failed / cancelled / skipped.",
          "annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": true
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Execution detail (or full detail if scope allows)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/ExecutionFull"
                        },
                        {
                          "$ref": "#/components/schemas/ExecutionDetail"
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1842,
                    "object": "execution",
                    "workflow_id": 27,
                    "status": "completed",
                    "execution_key": "api_client:12:3fa85f64-5717-4562-b3fc-2c963f66afa6",
                    "external_reference": "rv-app-20491",
                    "needs_human_review": false,
                    "review_status": "none",
                    "assigned_to_user_id": null,
                    "started_at": "2026-09-09T14:00:01Z",
                    "completed_at": "2026-09-09T14:00:09Z",
                    "created_at": "2026-09-09T14:00:00Z",
                    "updated_at": "2026-09-09T14:00:09Z",
                    "input_context": {
                      "applicant": {
                        "first_name": "Jordan",
                        "last_name": "Lee",
                        "email": "jordan@example.com"
                      },
                      "execution_mode": "test",
                      "submitted_by_user_id": 5,
                      "user_approved": false,
                      "external_reference": "rv-app-20491"
                    }
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/cancel": {
      "post": {
        "operationId": "cancelExecution",
        "tags": [
          "executions"
        ],
        "summary": "Cancel an execution",
        "description": "Cancels a `pending`, `running`, or `waiting` execution, any\noutstanding waits, and any unresolved adverse-action drafts. The\nengine notices between nodes and stops dispatching further work, so\na node already in flight may finish. Requires `executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-doc-page": "/docs/concepts/executions",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "responses": {
          "202": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ExecutionDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`execution_not_cancellable` (already terminal or awaiting review)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "execution_not_cancellable",
                    "message": "Execution is already completed.",
                    "details": [
                      {
                        "field": "status",
                        "message": "completed"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/execution_not_cancellable"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/re_evaluations": {
      "post": {
        "operationId": "reEvaluateExecution",
        "tags": [
          "executions"
        ],
        "summary": "Re-evaluate an execution",
        "description": "Creates a new execution that re-runs the source's `input_context`\nagainst the source's snapshotted definition (NOT the workflow's\ncurrent definition) and returns `202`; poll `getExecution` on the\nnew id. The source must belong to an active workflow, and re-running\na live-mode execution requires production access.\n\n`Idempotency-Key` is required and is namespaced per source\nexecution, so the same key can be reused across different sources.\nRequires `executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-idempotent": true,
        "x-async": {
          "poll": "getExecution"
        },
        "x-doc-page": "/docs/concepts/executions",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Re-evaluation accepted (the NEW execution)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ExecutionDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope` or `live_mode_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "live_mode_forbidden",
                    "message": "Re-running a live-mode execution requires production access.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/live_mode_forbidden"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`workflow_inactive`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "workflow_inactive",
                    "message": "Only active workflows can be re-evaluated through the public API.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/workflow_inactive"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`missing_idempotency_key`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "missing_idempotency_key",
                    "message": "Idempotency-Key header is required.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/missing_idempotency_key"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/review_decisions": {
      "post": {
        "operationId": "submitReviewDecision",
        "tags": [
          "executions"
        ],
        "summary": "Approve or deny a paused review",
        "description": "Records the decision on an execution that is `awaiting_review` (or\n`waiting`) with `needs_human_review: true`, auto-claims it for the\ncaller if unassigned, and resumes the chosen branch. Test-mode\nexecutions resume inline and return `200` with the final state;\nlive-mode executions enqueue the resume and return `202` with\n`resume_enqueued: true`. Requires `executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-doc-page": "/docs/concepts/human-review",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "requestBody": {
          "description": "The review decision (approve or deny) for the paused step.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReviewDecisionRequest"
              },
              "example": {
                "decision": "approved",
                "note": "Manual review confirmed; OFAC clean."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Review decided and downstream branch ran inline (test mode)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ExecutionDetail"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "resume_enqueued": {
                              "type": "boolean",
                              "example": false
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Review decided and downstream branch enqueued (live mode)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ExecutionDetail"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "resume_enqueued": {
                              "type": "boolean",
                              "example": true
                            }
                          }
                        }
                      ]
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope`, `review_assigned_elsewhere` or `live_mode_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "review_assigned_elsewhere": {
                    "value": {
                      "error": {
                        "code": "review_assigned_elsewhere",
                        "message": "This review is assigned to another user.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/review_assigned_elsewhere"
                      }
                    }
                  },
                  "live_mode_forbidden": {
                    "value": {
                      "error": {
                        "code": "live_mode_forbidden",
                        "message": "Live-mode review decisions require production access.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/live_mode_forbidden"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`review_not_pending` or `review_already_decided`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "review_not_pending": {
                    "value": {
                      "error": {
                        "code": "review_not_pending",
                        "message": "Execution is not awaiting human review.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/review_not_pending"
                      }
                    }
                  },
                  "review_already_decided": {
                    "value": {
                      "error": {
                        "code": "review_already_decided",
                        "message": "Review was already approved.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/review_already_decided"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`validation_failed` — `decision` must be `approved` or `denied`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The request could not be processed.",
                    "details": [
                      {
                        "field": "decision",
                        "message": "must be approved or denied"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/review_events": {
      "get": {
        "operationId": "listReviewEvents",
        "tags": [
          "executions"
        ],
        "summary": "List review audit events",
        "description": "Returns flagged / assigned / unassigned / approved / denied / note_added / re_evaluated events, plus the adverse_action_drafted / adverse_action_sent / adverse_action_skipped / adverse_action_replayed notice events, ordered by created_at. Requires `executions:read`.",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/human-review",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          },
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Audit trail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReviewEvent"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/review_assignment": {
      "post": {
        "operationId": "assignReview",
        "tags": [
          "executions"
        ],
        "summary": "Claim or unassign a review",
        "description": "`assignee: \"me\"` claims the review for the calling api_user;\n`assignee: null` releases it. An `assigned` / `unassigned` audit event\nis recorded when the assignee actually changes. Requires\n`executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-doc-page": "/docs/concepts/human-review",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "requestBody": {
          "description": "Whether to claim the review or unassign it.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReviewAssignmentRequest"
              },
              "examples": {
                "claim": {
                  "summary": "Claim the review for this api_user",
                  "value": {
                    "assignee": "me"
                  }
                },
                "unassign": {
                  "summary": "Release the review (no assignee)",
                  "value": {
                    "assignee": null
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated assignment",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ExecutionDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "`validation_failed` — `assignee` must be `\"me\"` or `null`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The request could not be processed.",
                    "details": [
                      {
                        "field": "assignee",
                        "message": "must be 'me' to claim or null to unassign"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/review_notes": {
      "post": {
        "operationId": "addReviewNote",
        "tags": [
          "executions"
        ],
        "summary": "Append a review note",
        "description": "Records a `note_added` audit event on the execution. Requires `executions:write`.",
        "x-scopes": [
          "executions:write"
        ],
        "x-doc-page": "/docs/concepts/human-review",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "requestBody": {
          "description": "The note to append to the review.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReviewNoteRequest"
              },
              "example": {
                "note": "Pulled credit report manually; matches OFAC cleanly."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Note recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReviewEvent"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "`validation_failed` — blank `note`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The request could not be processed.",
                    "details": [
                      {
                        "field": "note",
                        "message": "must not be blank"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/review_bulk_actions": {
      "post": {
        "operationId": "bulkReviewActions",
        "tags": [
          "executions"
        ],
        "summary": "Bulk approve / deny / assign_to_me",
        "description": "Always asynchronous. Approve/deny enqueue the resume per execution\n(`enqueued` counts them). Cap of 25 ids per request. Ids that do not\nresolve in the caller's scope are reported as `not_found` in\n`skipped` so existence is not leaked. Requires `executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-doc-page": "/docs/concepts/human-review",
        "requestBody": {
          "description": "The action to apply and the executions to apply it to.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkReviewRequest"
              },
              "example": {
                "execution_ids": [
                  1842,
                  1843,
                  1844,
                  1845,
                  1846
                ],
                "bulk_action": "approve",
                "note": "Manual review batch 2026-09-09 — OFAC and credit clean."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Bulk action processed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkReviewResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "object": "bulk_review_result",
                    "applied": 4,
                    "enqueued": 4,
                    "skipped": [
                      {
                        "id": 1846,
                        "reason": "not_found"
                      }
                    ]
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "description": "`validation_failed` — unknown `bulk_action`, empty `execution_ids`, or more than 25 ids",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "Bulk action exceeds the per-request maximum.",
                    "details": [
                      {
                        "field": "execution_ids",
                        "message": "max 25 ids per request"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/report_artifacts": {
      "get": {
        "operationId": "listReportArtifacts",
        "tags": [
          "executions"
        ],
        "summary": "List credit-report PDFs for an execution",
        "description": "Metadata only; the whole list is returned in one response (not\npaginated — `page` / `per_page` are ignored). Download each file\nwith `getReportArtifact`, which needs `executions:read:full`.\nRequires `executions:read`.\n",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/executions",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Artifact list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Artifact"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/StaticCollectionMeta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 9911,
                      "object": "report_artifact",
                      "execution_id": 1842,
                      "filename": "Equifax_Credit_Report.pdf",
                      "content_type": "application/pdf",
                      "byte_size": 184223,
                      "generated_at": "2026-09-09T14:00:07Z"
                    }
                  ],
                  "meta": {
                    "object": "report_artifact",
                    "total_count": 1,
                    "page": 1,
                    "per_page": 1,
                    "total_pages": 1,
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/report_artifacts/{artifact_id}": {
      "get": {
        "operationId": "getReportArtifact",
        "tags": [
          "executions"
        ],
        "summary": "Download a credit-report PDF",
        "description": "Returns the binary PDF as an attachment. Requires `executions:read:full` because the contents are raw applicant data.",
        "x-scopes": [
          "executions:read:full"
        ],
        "x-doc-page": "/docs/concepts/executions",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          },
          {
            "$ref": "#/components/parameters/ArtifactIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Binary PDF",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "example": "attachment; filename=\"Equifax_Credit_Report.pdf\""
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/notice_artifacts": {
      "get": {
        "operationId": "listNoticeArtifacts",
        "tags": [
          "executions"
        ],
        "summary": "List adverse-action notice PDFs for an execution",
        "description": "Metadata only; not paginated (`page` / `per_page` are ignored).\nDownload each file with `getNoticeArtifact`, which needs\n`executions:read:full`. Requires `executions:read`.\n",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/adverse-action",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Artifact list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Artifact"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/StaticCollectionMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/executions/{id}/notice_artifacts/{artifact_id}": {
      "get": {
        "operationId": "getNoticeArtifact",
        "tags": [
          "executions"
        ],
        "summary": "Download an adverse-action notice PDF",
        "description": "Returns the binary PDF as an attachment. Requires `executions:read:full`.",
        "x-scopes": [
          "executions:read:full"
        ],
        "x-doc-page": "/docs/concepts/adverse-action",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExecutionIdParam"
          },
          {
            "$ref": "#/components/parameters/ArtifactIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Binary PDF",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "example": "attachment; filename=\"Adverse_Action_Notice.pdf\""
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/workflows/{id}/batches": {
      "post": {
        "operationId": "createBatch",
        "tags": [
          "batches"
        ],
        "summary": "Create a batch from N rows",
        "description": "Pre-creates the batch and N `queued` child executions in a single\ntransaction and returns `202`; the rows are then processed\nasynchronously. Poll `getBatch` for the counters and\n`listBatchExecutions` for per-row status. Cap of 500 rows per\nrequest; every row must be an object. `live` requires production\naccess (`403 live_mode_forbidden`).\n\nBatches need an **active** cascade, in test mode too. On a draft\n(`active: false`) every batch create returns `409 workflow_inactive`\nbefore the rows, the mode or the `Idempotency-Key` are checked. A\nsingle test-mode `triggerWorkflow` on a draft still runs (`202`).\nA sandbox account (not approved for production) cannot activate a\ncascade, so it gets this 409 on every batch. To run a batch, an\napproved account activates the cascade in the app, or with\n`PATCH /workflows/{id}` and `active: true` from a key made at\n/api-keys (docs keys cannot activate).\n\n`Idempotency-Key` is required (`422 missing_idempotency_key`). The\nkey is compared against the resolved `execution_mode`,\n`external_reference` and a SHA-256 digest of the rows, so reusing a\nkey with different content returns `409 idempotency_conflict`.\nRequires `executions:write`.\n",
        "x-scopes": [
          "executions:write"
        ],
        "x-idempotent": true,
        "x-async": {
          "poll": "getBatch"
        },
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkflowIdParam"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "description": "The rows to run, one applicant payload per row.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchCreateRequest"
              },
              "example": {
                "rows": [
                  {
                    "external_reference": "row-1",
                    "applicant": {
                      "first_name": "Ava",
                      "last_name": "Stone",
                      "email": "ava@example.com"
                    }
                  },
                  {
                    "external_reference": "row-2",
                    "applicant": {
                      "first_name": "Ben",
                      "last_name": "Park",
                      "email": "ben@example.com"
                    }
                  }
                ],
                "execution_mode": "test",
                "external_reference": "rentvine-batch-001"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 77,
                    "object": "batch",
                    "workflow_id": 27,
                    "status": "pending",
                    "execution_mode": "test",
                    "external_reference": "rentvine-batch-001",
                    "total_count": 2,
                    "completed_count": 0,
                    "failed_count": 0,
                    "started_at": null,
                    "completed_at": null,
                    "created_at": "2026-09-09T14:20:00Z",
                    "updated_at": "2026-09-09T14:20:00Z"
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`malformed_body`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope` or `live_mode_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "live_mode_forbidden",
                    "message": "Live mode requires production access.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/live_mode_forbidden"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`workflow_inactive` (the cascade is a draft, in any mode) or `idempotency_conflict`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "workflow_inactive": {
                    "value": {
                      "error": {
                        "code": "workflow_inactive",
                        "message": "Only active workflows can run batches through the public API.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/workflow_inactive"
                      }
                    }
                  },
                  "idempotency_conflict": {
                    "value": {
                      "error": {
                        "code": "idempotency_conflict",
                        "message": "The Idempotency-Key has already been used with a different request.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/idempotency_conflict"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`missing_idempotency_key` or `validation_failed` (empty `rows`, more than 500 rows, a non-object row, or a bad `execution_mode`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "missing_idempotency_key": {
                    "value": {
                      "error": {
                        "code": "missing_idempotency_key",
                        "message": "Idempotency-Key header is required.",
                        "details": [],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/missing_idempotency_key"
                      }
                    }
                  },
                  "too_many_rows": {
                    "value": {
                      "error": {
                        "code": "validation_failed",
                        "message": "Batch size exceeds the per-request maximum.",
                        "details": [
                          {
                            "field": "rows",
                            "message": "max 500 rows per batch"
                          }
                        ],
                        "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                        "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches": {
      "get": {
        "operationId": "listBatches",
        "tags": [
          "batches"
        ],
        "summary": "List batches",
        "description": "Batches created by the calling api_client, newest first. Requires `executions:read`.",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          },
          {
            "name": "filter[status]",
            "in": "query",
            "description": "CSV of `pending, running, interrupted, completed, cancelled`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[workflow_id]",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "filter[external_reference]",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Batch"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}": {
      "get": {
        "operationId": "getBatch",
        "tags": [
          "batches"
        ],
        "summary": "Show a batch",
        "description": "Batch status and counters. This is the endpoint to poll after\n`createBatch`, `resumeBatch` and `retryFailedBatch`; the batch is\nfinished when `status` is `completed` or `cancelled` (`interrupted`\nmeans it stopped early and can be resumed). Requires `executions:read`.\n",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Batch detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/executions": {
      "get": {
        "operationId": "listBatchExecutions",
        "tags": [
          "batches"
        ],
        "summary": "List child executions of a batch",
        "description": "Child rows ordered by `row_index`. Tokens with `executions:read:full`\nadditionally receive `applicant_name` and `applicant_email`.\nRequires `executions:read`.\n",
        "x-scopes": [
          "executions:read"
        ],
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          },
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          },
          {
            "name": "filter[status]",
            "in": "query",
            "description": "CSV of execution statuses",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Child execution list (row_index ordered)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BatchExecutionRow"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/cancel": {
      "post": {
        "operationId": "cancelBatch",
        "tags": [
          "batches"
        ],
        "summary": "Cancel a batch",
        "description": "Halts dispatch and propagates `cancelled` to in-flight and queued child executions. Requires `executions:write`.",
        "x-scopes": [
          "executions:write"
        ],
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          }
        ],
        "responses": {
          "202": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`batch_not_cancellable` (not `pending`, `running` or `interrupted`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "batch_not_cancellable",
                    "message": "Batch is in status completed and cannot be cancelled.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/batch_not_cancellable"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/resume": {
      "post": {
        "operationId": "resumeBatch",
        "tags": [
          "batches"
        ],
        "summary": "Resume an interrupted batch",
        "description": "Re-queues remaining children and restarts processing; poll `getBatch`. Re-checks `workflow_inactive` and `live_mode_forbidden` at resume time. Requires `executions:write`.",
        "x-scopes": [
          "executions:write"
        ],
        "x-async": {
          "poll": "getBatch"
        },
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          }
        ],
        "responses": {
          "202": {
            "description": "Resumed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope` or `live_mode_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`batch_not_resumable` or `workflow_inactive`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "batch_not_resumable",
                    "message": "Batch is in status running and cannot be resumed.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/batch_not_resumable"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/retry_failed": {
      "post": {
        "operationId": "retryFailedBatch",
        "tags": [
          "batches"
        ],
        "summary": "Retry failed and skipped rows",
        "description": "Bumps `retry_count` on failed rows under the per-row cap, requeues skipped rows, clears their stale terminal state, and restarts processing; poll `getBatch`. Re-checks `workflow_inactive` and `live_mode_forbidden`. Requires `executions:write`.",
        "x-scopes": [
          "executions:write"
        ],
        "x-async": {
          "poll": "getBatch"
        },
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          }
        ],
        "responses": {
          "202": {
            "description": "Retry started",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`insufficient_scope` or `live_mode_forbidden`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`batch_not_retryable` or `workflow_inactive`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "batch_not_retryable",
                    "message": "Batch is in status completed and has no retryable rows.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/batch_not_retryable"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/exports.csv": {
      "get": {
        "operationId": "exportBatch",
        "tags": [
          "batches"
        ],
        "summary": "Download flat results CSV",
        "description": "One row per child execution by default. Pass `?include=all` to\ninclude re-evaluation revisions. The same route without an\nextension — `GET /batches/{id}/exports` — also returns CSV (it is\nthe default format); `.zip` selects the structured archive\n(`exportBatchZip`). Requires `executions:read:full` because the CSV\ncontains raw applicant data.\n",
        "x-scopes": [
          "executions:read:full"
        ],
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          },
          {
            "name": "include",
            "in": "query",
            "description": "`all` includes every re-evaluation revision of each row.",
            "schema": {
              "type": "string",
              "enum": [
                "all"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "example": "attachment; filename=\"batch_77_results_2026-09-09.csv\""
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "UTF-8 CSV text with a header row."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/exports.zip": {
      "get": {
        "operationId": "exportBatchZip",
        "tags": [
          "batches"
        ],
        "summary": "Download structured ZIP of batch results",
        "description": "ZIP containing an error report, per-product CSVs, JSONs, and\nreport PDFs. Pass `?include=all` for revision history. A PDF whose\nfile is no longer stored is left out and named in `MissingFiles.txt`.\nRequires `executions:read:full`.\n",
        "x-scopes": [
          "executions:read:full"
        ],
        "x-doc-page": "/docs/concepts/batches",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          },
          {
            "name": "include",
            "in": "query",
            "description": "`all` includes every re-evaluation revision of each row.",
            "schema": {
              "type": "string",
              "enum": [
                "all"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "ZIP",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "example": "attachment; filename=\"batch_77_results_2026-09-09.zip\""
              }
            },
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/batches/{id}/adverse_action_notices.zip": {
      "get": {
        "operationId": "downloadBatchAdverseActionNotices",
        "tags": [
          "batches"
        ],
        "summary": "Download all adverse-action notice PDFs as a ZIP",
        "description": "One entry per notice, prefixed with the applicant name. A notice whose file is no longer stored is left out and named in `MissingFiles.txt`. Returns `404 not_found` when no notices exist for the batch. Requires `executions:read:full`.",
        "x-scopes": [
          "executions:read:full"
        ],
        "x-doc-page": "/docs/concepts/adverse-action",
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "ZIP",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "example": "attachment; filename=\"adverse_action_notices_batch_77.zip\""
              }
            },
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "description": "Batch not found, or no notices have been generated for it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "No adverse-action notices have been generated for this batch.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/not_found"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook_subscriptions": {
      "get": {
        "operationId": "listWebhookSubscriptions",
        "tags": [
          "webhooks"
        ],
        "summary": "List webhook subscriptions",
        "description": "Subscriptions belonging to the calling api_client, newest first. `signing_secret` is never included. Requires `webhooks:read`.",
        "x-scopes": [
          "webhooks:read"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription list (signing_secret never exposed here)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookSubscription"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createWebhookSubscription",
        "tags": [
          "webhooks"
        ],
        "summary": "Create a webhook subscription",
        "description": "Returns the `signing_secret` EXACTLY ONCE. Store it; subsequent\nreads never re-expose it (use `rotateWebhookSecret` if you lose it).\nThe URL is validated against the SSRF guard (no private / loopback /\ncloud-metadata addresses, no blocked service ports). An empty or\nomitted `event_filter` subscribes to every event. Requires\n`webhooks:write`.\n",
        "x-scopes": [
          "webhooks:write"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "requestBody": {
          "description": "The endpoint URL and the events to deliver to it.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSubscriptionCreateRequest"
              },
              "example": {
                "url": "https://hooks.partner.example/cascades",
                "event_filter": [
                  "execution.completed",
                  "execution.failed",
                  "adverse_action.completed"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created (response includes signing_secret)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookSubscriptionWithSecret"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 9,
                    "object": "webhook_subscription",
                    "url": "https://hooks.partner.example/cascades",
                    "event_filter": [
                      "execution.completed",
                      "execution.failed",
                      "adverse_action.completed"
                    ],
                    "active": true,
                    "last_delivered_at": null,
                    "created_at": "2026-09-09T13:00:00Z",
                    "updated_at": "2026-09-09T13:00:00Z",
                    "signing_secret": "whsec_4f1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9"
                  },
                  "meta": {
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "description": "`validation_failed` — bad URL, non-public host, or an unknown event name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "validation_failed",
                    "message": "The request could not be processed.",
                    "details": [
                      {
                        "field": "event_filter",
                        "message": "contains unknown event: execution.started"
                      }
                    ],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/validation_failed"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook_subscriptions/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSubscriptionIdParam"
        }
      ],
      "get": {
        "operationId": "getWebhookSubscription",
        "tags": [
          "webhooks"
        ],
        "summary": "Show a webhook subscription",
        "description": "Requires `webhooks:read`.",
        "x-scopes": [
          "webhooks:read"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "responses": {
          "200": {
            "description": "Subscription (no signing_secret)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookSubscription"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateWebhookSubscription",
        "tags": [
          "webhooks"
        ],
        "summary": "Update a webhook subscription",
        "description": "Partial update of `url`, `event_filter` and `active`; omitted keys\nkeep their value. `PUT /webhook_subscriptions/{id}` is accepted as an\nalias with the same partial semantics. The response never includes\nthe `signing_secret`. Requires `webhooks:write`.\n",
        "x-scopes": [
          "webhooks:write"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "requestBody": {
          "description": "The fields to change. Omitted fields keep their current value.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSubscriptionUpdateRequest"
              },
              "examples": {
                "narrow_filter": {
                  "summary": "Subscribe to fewer events",
                  "value": {
                    "event_filter": [
                      "adverse_action.completed"
                    ]
                  }
                },
                "pause": {
                  "summary": "Pause the subscription without deleting it",
                  "value": {
                    "active": false
                  }
                },
                "new_url": {
                  "summary": "Move the subscription to a new URL",
                  "value": {
                    "url": "https://hooks.partner.example/cascades/v2"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated (no signing_secret)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookSubscription"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhookSubscription",
        "tags": [
          "webhooks"
        ],
        "summary": "Delete a webhook subscription",
        "description": "Permanently deletes the subscription and its delivery log. Requires `webhooks:write`.",
        "x-scopes": [
          "webhooks:write"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "responses": {
          "204": {
            "description": "Deleted (no body)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook_subscriptions/{id}/rotate_secret": {
      "post": {
        "operationId": "rotateWebhookSecret",
        "tags": [
          "webhooks"
        ],
        "summary": "Rotate the signing secret",
        "description": "Generates a new `signing_secret` (returned once, in this response)\nand preserves the previous one for 24 hours so deliveries during\nyour deploy window ship with both `v1=` (new) and `v0=` (old)\nsignatures. After the grace window only `v1=` ships. Requires\n`webhooks:write`.\n",
        "x-scopes": [
          "webhooks:write"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookSubscriptionIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "New signing_secret returned (one-time)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookSubscriptionWithSecret"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook_deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "tags": [
          "webhooks"
        ],
        "summary": "List webhook deliveries",
        "description": "Delivery attempts across all of the calling api_client's\nsubscriptions, newest first. The event payload and the subscriber's\nresponse body are deliberately omitted. CSV filters accept several\ncomma-separated values; unknown values return `422`. Requires\n`webhooks:read`.\n",
        "x-scopes": [
          "webhooks:read"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParam"
          },
          {
            "$ref": "#/components/parameters/PerPageParam"
          },
          {
            "name": "filter[webhook_subscription_id]",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "filter[event_type]",
            "in": "query",
            "description": "CSV of event names",
            "schema": {
              "type": "string"
            },
            "example": "execution.completed,execution.failed"
          },
          {
            "name": "filter[status]",
            "in": "query",
            "description": "CSV of `pending, retrying, succeeded, failed, dead`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginatedMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook_deliveries/{id}": {
      "get": {
        "operationId": "getWebhookDelivery",
        "tags": [
          "webhooks"
        ],
        "summary": "Show a webhook delivery",
        "description": "Requires `webhooks:read`.",
        "x-scopes": [
          "webhooks:read"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookDeliveryIdParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook_deliveries/{id}/retry": {
      "post": {
        "operationId": "retryWebhookDelivery",
        "tags": [
          "webhooks"
        ],
        "summary": "Manually retry a failed or dead delivery",
        "description": "Resets the delivery to `pending` and re-enqueues it immediately, outside the automatic retry schedule. Refuses to re-deliver a row that already succeeded or is currently in flight. Requires `webhooks:write`.",
        "x-scopes": [
          "webhooks:write"
        ],
        "x-doc-page": "/docs/concepts/webhooks",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookDeliveryIdParam"
          }
        ],
        "responses": {
          "202": {
            "description": "Retry enqueued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`delivery_not_retryable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "delivery_not_retryable",
                    "message": "Delivery is in status succeeded and cannot be retried.",
                    "details": [],
                    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
                    "doc_url": "https://cascades.sandbox.crs-data.com/docs/api/errors/delivery_not_retryable"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  }
}