Quickstart
Mint a sandbox key, trigger your first cascade and read the decision in under five minutes — with curl, Node, Python, Ruby or an MCP agent.
Five minutes from zero to a completed screening run. Everything below runs in test mode: synthetic report data, no bureau calls, no charges — and it works on draft cascades, so you do not need an activated account.
-
Mint a sandbox key
Open /api-keys and click Generate key (it reads Regenerate key when you already have one, and regenerating revokes the old key). The raw token is shown exactly once — copy it somewhere safe. Sandbox keys carry every scope (
workflows:read,workflows:write,executions:read,executions:read:full,executions:write,workflow_templates:read,webhooks:read,webhooks:write) and expire after 90 days.Send it as a bearer token on every request:
HTTPAuthorization: Bearer {{API_KEY}} -
Find a cascade to run
List the cascades in your workspace. If you have none yet, build one in the visual builder at /cascades, copy a template, or generate one from a sentence.
cURLcurl "https://cascades.crscreditapi.com/api/v1/workflows" \ -H "Authorization: Bearer {{API_KEY}}"Node.jsconst response = await fetch("https://cascades.crscreditapi.com/api/v1/workflows", { method: "GET", headers: { "Authorization": "Bearer {{API_KEY}}" } }); const { data, meta } = await response.json(); console.log(response.status, data, meta.request_id);Pythonimport requests response = requests.get( "https://cascades.crscreditapi.com/api/v1/workflows", headers={ "Authorization": "Bearer {{API_KEY}}", }, ) response.raise_for_status() payload = response.json() print(payload["data"], payload["meta"]["request_id"])Rubyrequire "net/http" require "json" uri = URI("https://cascades.crscreditapi.com/api/v1/workflows") request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer {{API_KEY}}" response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http| http.request(request) end payload = JSON.parse(response.body) puts payload["data"], payload.dig("meta", "request_id")Gopackage main import ( "fmt" "io" "net/http" ) func main() { req, err := http.NewRequest("GET", "https://cascades.crscreditapi.com/api/v1/workflows", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer {{API_KEY}}") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }Agentclaude mcp add cascades \ -e CASCADES_API_KEY={{API_KEY}} \ -e CASCADES_PROD_BASE_URL=https://cascades.crscreditapi.com \ -- npx -y @crscreditapi/cascades-mcp-server # Then ask your assistant: # "List workflows and summarise the result." # It will call the `cascades_list_workflows` tool with your key.Note the
idof the cascade you want — the examples below use27. -
Trigger a run
POST /workflows/{id}/triggerstarts an execution. Three things matter:Idempotency-Keyis required. Use something unique to the business event (an application id). Re-sending the same key and body returns the original execution instead of starting a second one.execution_modedefaults totest. Leave it there until your account is approved for live runs.external_referenceis your own id; it comes back on the execution and on every webhook.
cURLcurl -X POST "https://cascades.crscreditapi.com/api/v1/workflows/27/trigger" \ -H "Authorization: Bearer {{API_KEY}}" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \ -d '{ "payload": { "applicant": { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com" } }, "execution_mode": "test", "external_reference": "rv-app-20491" }'Node.jsconst response = await fetch("https://cascades.crscreditapi.com/api/v1/workflows/27/trigger", { method: "POST", headers: { "Authorization": "Bearer {{API_KEY}}", "Content-Type": "application/json", "Idempotency-Key": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }, body: JSON.stringify({ "payload": { "applicant": { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com" } }, "execution_mode": "test", "external_reference": "rv-app-20491" }) }); const { data, meta } = await response.json(); console.log(response.status, data, meta.request_id);Pythonimport requests response = requests.post( "https://cascades.crscreditapi.com/api/v1/workflows/27/trigger", headers={ "Authorization": "Bearer {{API_KEY}}", "Idempotency-Key": "3fa85f64-5717-4562-b3fc-2c963f66afa6", }, json={ "payload": { "applicant": { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com" } }, "execution_mode": "test", "external_reference": "rv-app-20491" }, ) response.raise_for_status() payload = response.json() print(payload["data"], payload["meta"]["request_id"])Rubyrequire "net/http" require "json" uri = URI("https://cascades.crscreditapi.com/api/v1/workflows/27/trigger") request = Net::HTTP::Post.new(uri) request["Authorization"] = "Bearer {{API_KEY}}" request["Content-Type"] = "application/json" request["Idempotency-Key"] = "3fa85f64-5717-4562-b3fc-2c963f66afa6" request.body = <<~JSON { "payload": { "applicant": { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com" } }, "execution_mode": "test", "external_reference": "rv-app-20491" } JSON response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http| http.request(request) end payload = JSON.parse(response.body) puts payload["data"], payload.dig("meta", "request_id")Gopackage main import ( "bytes" "fmt" "io" "net/http" ) func main() { body := []byte(`{ "payload": { "applicant": { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com" } }, "execution_mode": "test", "external_reference": "rv-app-20491" }`) req, err := http.NewRequest("POST", "https://cascades.crscreditapi.com/api/v1/workflows/27/trigger", bytes.NewBuffer(body)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer {{API_KEY}}") req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "3fa85f64-5717-4562-b3fc-2c963f66afa6") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }Agentclaude mcp add cascades \ -e CASCADES_API_KEY={{API_KEY}} \ -e CASCADES_PROD_BASE_URL=https://cascades.crscreditapi.com \ -- npx -y @crscreditapi/cascades-mcp-server # Then ask your assistant: # "Trigger a workflow 27 and summarise the result." # It will call the `cascades_trigger_workflow` tool with your key.The response is
202 Acceptedwith the new execution —statuswill bepending, not the final answer.Try it
POST/workflows/{id}/triggerSign in to run this against your sandbox — the console uses a key that only ever runs in test mode.
Sign in to try it -
Poll for the decision
Cascades are asynchronous. Poll
GET /executions/{id}every second or two untilstatusleavespending/running/waiting(queuedonly appears on batch child rows — see Batches). Terminal statuses arecompleted,failed,cancelledandskipped;awaiting_reviewmeans a person has to decide (see Handle human review).cURLcurl "https://cascades.crscreditapi.com/api/v1/executions/1842" \ -H "Authorization: Bearer {{API_KEY}}"Node.jsconst response = await fetch("https://cascades.crscreditapi.com/api/v1/executions/1842", { method: "GET", headers: { "Authorization": "Bearer {{API_KEY}}" } }); const { data, meta } = await response.json(); console.log(response.status, data, meta.request_id);Pythonimport requests response = requests.get( "https://cascades.crscreditapi.com/api/v1/executions/1842", headers={ "Authorization": "Bearer {{API_KEY}}", }, ) response.raise_for_status() payload = response.json() print(payload["data"], payload["meta"]["request_id"])Rubyrequire "net/http" require "json" uri = URI("https://cascades.crscreditapi.com/api/v1/executions/1842") request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer {{API_KEY}}" response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http| http.request(request) end payload = JSON.parse(response.body) puts payload["data"], payload.dig("meta", "request_id")Gopackage main import ( "fmt" "io" "net/http" ) func main() { req, err := http.NewRequest("GET", "https://cascades.crscreditapi.com/api/v1/executions/1842", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer {{API_KEY}}") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }Agentclaude mcp add cascades \ -e CASCADES_API_KEY={{API_KEY}} \ -e CASCADES_PROD_BASE_URL=https://cascades.crscreditapi.com \ -- npx -y @crscreditapi/cascades-mcp-server # Then ask your assistant: # "Show an execution 1842 and summarise the result." # It will call the `cascades_get_execution` tool with your key.With
executions:read:fullthe payload also carriesstep_outputs— the per-node results, including the report data the decision was made on — andfailed_steps. -
Stop polling: subscribe to webhooks
Polling is fine for a first run; in production subscribe once and let Cascades call you.
POST /webhook_subscriptionsreturns asigning_secretexactly once. Every delivery is signed; verify it before trusting the payload.cURLcurl -X POST "https://cascades.crscreditapi.com/api/v1/webhook_subscriptions" \ -H "Authorization: Bearer {{API_KEY}}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://hooks.partner.example/cascades", "event_filter": [ "execution.completed", "execution.failed", "adverse_action.completed" ] }'Node.jsconst response = await fetch("https://cascades.crscreditapi.com/api/v1/webhook_subscriptions", { method: "POST", headers: { "Authorization": "Bearer {{API_KEY}}", "Content-Type": "application/json" }, body: JSON.stringify({ "url": "https://hooks.partner.example/cascades", "event_filter": [ "execution.completed", "execution.failed", "adverse_action.completed" ] }) }); const { data, meta } = await response.json(); console.log(response.status, data, meta.request_id);Pythonimport requests response = requests.post( "https://cascades.crscreditapi.com/api/v1/webhook_subscriptions", headers={ "Authorization": "Bearer {{API_KEY}}", }, json={ "url": "https://hooks.partner.example/cascades", "event_filter": [ "execution.completed", "execution.failed", "adverse_action.completed" ] }, ) response.raise_for_status() payload = response.json() print(payload["data"], payload["meta"]["request_id"])Rubyrequire "net/http" require "json" uri = URI("https://cascades.crscreditapi.com/api/v1/webhook_subscriptions") request = Net::HTTP::Post.new(uri) request["Authorization"] = "Bearer {{API_KEY}}" request["Content-Type"] = "application/json" request.body = <<~JSON { "url": "https://hooks.partner.example/cascades", "event_filter": [ "execution.completed", "execution.failed", "adverse_action.completed" ] } JSON response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http| http.request(request) end payload = JSON.parse(response.body) puts payload["data"], payload.dig("meta", "request_id")Gopackage main import ( "bytes" "fmt" "io" "net/http" ) func main() { body := []byte(`{ "url": "https://hooks.partner.example/cascades", "event_filter": [ "execution.completed", "execution.failed", "adverse_action.completed" ] }`) req, err := http.NewRequest("POST", "https://cascades.crscreditapi.com/api/v1/webhook_subscriptions", bytes.NewBuffer(body)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer {{API_KEY}}") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }Agentclaude mcp add cascades \ -e CASCADES_API_KEY={{API_KEY}} \ -e CASCADES_PROD_BASE_URL=https://cascades.crscreditapi.com \ -- npx -y @crscreditapi/cascades-mcp-server # Then ask your assistant: # "Create a webhook subscription using the Cascades API." # No MCP tool covers this endpoint yet; the assistant can call POST /webhook_subscriptions with curl.See Webhooks for the event catalog and Verify webhook signatures for drop-in verifiers.
Prefer to let an agent do it?
Install the MCP server and ask your assistant in plain English. The server wraps the same endpoints with the same key and scopes.
claude mcp add cascades \
-e CASCADES_API_KEY={{API_KEY}} \
-e CASCADES_PROD_BASE_URL=https://cascades.crscreditapi.com \
-- npx -y @crscreditapi/cascades-mcp-serverThen: "List my cascades, run cascade 27 for a registered test subject in test mode, and explain the decision." Details and per-client install pages are under MCP server.
What next?
- Your first screening — the full flow with a test identity, a chosen mock outcome, human review and adverse action.
- Sandbox vs live — what changes when you flip
execution_mode. - API reference — every endpoint with request and response schemas.