---
title: Your first screening
description: Step by step from a fresh sandbox key to a decided execution — trigger a test subject, pick the mock outcome, poll, handle review, and read the notice.
canonical_url: "https://cascades.crscreditapi.com/docs/guides/first-screening"
doc_version: "1.0.0"
last_updated: "2026-10-05T19:24:37Z"
source: docs/guides/first-screening.md.erb
---

# Your first screening

This guide runs one applicant through a cascade end to end in the sandbox: pick a registered test subject for a realistic payload, choose the mock outcome, trigger, poll, decide a human review if the cascade pauses, and read what came out. Everything is `execution_mode: test`, so nothing is billed and no bureau is called.


> **Before you start**
>
> You need a sandbox key from [/api-keys](/api-keys) and at least one cascade in your workspace (build one at [/cascades](/cascades) or [generate one](/docs/guides/ai-generate-a-cascade)). Drafts are fine — a draft accepts test-mode triggers.



### Pick the cascade and a test subject

List your cascades and note an `id`. Then ask for the subjects registered for that cascade's products — each comes with a `payload` shaped for the trigger endpoint:


**cURL**

```shell
curl "https://cascades.crscreditapi.com/api/v1/workflows/27/test_cases" \
  -H "Authorization: Bearer {{API_KEY}}"
```

**Node.js**

```javascript
const response = await fetch("https://cascades.crscreditapi.com/api/v1/workflows/27/test_cases", {
  method: "GET",
  headers: {
    "Authorization": "Bearer {{API_KEY}}"
  }
});

const { data, meta } = await response.json();
console.log(response.status, data, meta.request_id);
```

**Python**

```python
import requests

response = requests.get(
    "https://cascades.crscreditapi.com/api/v1/workflows/27/test_cases",
    headers={
        "Authorization": "Bearer {{API_KEY}}",
    },
)
response.raise_for_status()
payload = response.json()
print(payload["data"], payload["meta"]["request_id"])
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://cascades.crscreditapi.com/api/v1/workflows/27/test_cases")
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")
```

**Go**

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://cascades.crscreditapi.com/api/v1/workflows/27/test_cases", 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))
}
```

**Agent**

```shell
claude 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 sandbox test cases for a workflow 27 and summarise the result."
# It will call the `cascades_list_test_cases` tool with your key.
```


Pick any subject. When the subject is registered for every product node in your cascade, the test run replays its authored history (a borderline credit file, a criminal record, an eviction) at no charge, and `input_context.sandbox_replay` is `true`; otherwise every product node returns its schema mock. See [Test identities](/docs/sandbox/test-identities). If the list is empty the registry could not be reached or the cascade has no covered products; any hand-written applicant works just as well in test mode.

### Trigger the run

Send the subject's `payload` as the trigger body with a unique `Idempotency-Key` (use your own application id) and an `external_reference` you can correlate on later:


**cURL**

```shell
curl -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.js**

```javascript
const 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);
```

**Python**

```python
import 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"])
```

**Ruby**

```ruby
require "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")
```

**Go**

```go
package 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))
}
```

**Agent**

```shell
claude 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.
```


For an applicant that is not a replayed subject, every product node returns its clean `happy_path` mock by default and the applicant is approved. To walk the decline path in test mode, add `"scenario": "fail_path"` to `payload` — every product node then returns its decline mock (the eviction node, for example, reports two filings and `recommendation: FAIL`):

```json
{
  "payload": {
    "scenario": "fail_path",
    "applicant": { "first_name": "Raymond", "last_name": "Okafor", "ssn": "…", "dob": "1982-01-01" },
    "property": { "address": "1803 Norma", "city": "Cottonwood", "state": "CA", "zip": "91502" }
  },
  "execution_mode": "test",
  "external_reference": "rv-app-20493"
}
```

Expect `202 Accepted`. Keep `data.id` — that is the execution — and `meta.request_id` for support.


> Try it in the browser: https://cascades.crscreditapi.com/docs/api/workflows/trigger-workflow — a signed-in sandbox key runs `POST /workflows/{id}/trigger` against the test identities.



### Poll until it settles

Poll every second or two until `status` is `completed`, `failed`, `cancelled`, `skipped`, or `awaiting_review`:


**cURL**

```shell
curl "https://cascades.crscreditapi.com/api/v1/executions/1842" \
  -H "Authorization: Bearer {{API_KEY}}"
```

**Node.js**

```javascript
const 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);
```

**Python**

```python
import 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"])
```

**Ruby**

```ruby
require "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")
```

**Go**

```go
package 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))
}
```

**Agent**

```shell
claude 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:full` the response includes `step_outputs`: one entry per node, keyed by node id, holding the report fields the decision used (for the credit subject, `creditScore.fico9` and the inquiry counts) and which branch each `IfElse` took.

```json
{
  "status": "completed",
  "step_outputs": {
    "credit": { "creditScore.fico9": 660, "summary.recentInquiries": 0, "recommendation": "PASS" },
    "decide": { "matched_branch": "pass", "conditions": [{ "field": "creditScore.fico9", "operator": "greater_than_or_equal", "value": 640, "result": true }] },
    "approve": { "decision": "approved" }
  }
}
```

### If it pauses: decide the review

A cascade with a human-review branch stops at `awaiting_review`. Approve or deny it — in test mode the branch runs inline and the response is the final state:


**cURL**

```shell
curl -X POST "https://cascades.crscreditapi.com/api/v1/executions/1842/review_decisions" \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "approved",
    "note": "Manual review confirmed; OFAC clean."
  }'
```

**Node.js**

```javascript
const response = await fetch("https://cascades.crscreditapi.com/api/v1/executions/1842/review_decisions", {
  method: "POST",
  headers: {
    "Authorization": "Bearer {{API_KEY}}",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "decision": "approved",
    "note": "Manual review confirmed; OFAC clean."
  })
});

const { data, meta } = await response.json();
console.log(response.status, data, meta.request_id);
```

**Python**

```python
import requests

response = requests.post(
    "https://cascades.crscreditapi.com/api/v1/executions/1842/review_decisions",
    headers={
        "Authorization": "Bearer {{API_KEY}}",
    },
    json={
      "decision": "approved",
      "note": "Manual review confirmed; OFAC clean."
    },
)
response.raise_for_status()
payload = response.json()
print(payload["data"], payload["meta"]["request_id"])
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://cascades.crscreditapi.com/api/v1/executions/1842/review_decisions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer {{API_KEY}}"
request["Content-Type"] = "application/json"
request.body = <<~JSON
  {
    "decision": "approved",
    "note": "Manual review confirmed; OFAC clean."
  }
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")
```

**Go**

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body := []byte(`{
	  "decision": "approved",
	  "note": "Manual review confirmed; OFAC clean."
	}`)
	req, err := http.NewRequest("POST", "https://cascades.crscreditapi.com/api/v1/executions/1842/review_decisions", 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))
}
```

**Agent**

```shell
claude 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:
#   "Approve or deny a paused review 1842 using the Cascades API."
# No MCP tool covers this endpoint yet; the assistant can call POST /executions/{id}/review_decisions with curl.
```


`approved` continues down `pass_steps`; `denied` continues down `fail_steps`; an adverse-action node in `human_review_steps` prepared its notice draft when the run was flagged. The whole decision trail is at `GET /executions/{id}/review_events`. More in [Handle human review](/docs/guides/handle-human-review).

### Read the artifacts

If the cascade pulled a credit report, its PDF is listed at `GET /executions/{id}/report_artifacts`; if it declined the applicant with an adverse-action node, the notice PDFs are at `GET /executions/{id}/notice_artifacts`. Listing needs `executions:read`; downloading a file needs `executions:read:full`.


**cURL**

```shell
curl "https://cascades.crscreditapi.com/api/v1/executions/1842/notice_artifacts" \
  -H "Authorization: Bearer {{API_KEY}}"
```

**Node.js**

```javascript
const response = await fetch("https://cascades.crscreditapi.com/api/v1/executions/1842/notice_artifacts", {
  method: "GET",
  headers: {
    "Authorization": "Bearer {{API_KEY}}"
  }
});

const { data, meta } = await response.json();
console.log(response.status, data, meta.request_id);
```

**Python**

```python
import requests

response = requests.get(
    "https://cascades.crscreditapi.com/api/v1/executions/1842/notice_artifacts",
    headers={
        "Authorization": "Bearer {{API_KEY}}",
    },
)
response.raise_for_status()
payload = response.json()
print(payload["data"], payload["meta"]["request_id"])
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://cascades.crscreditapi.com/api/v1/executions/1842/notice_artifacts")
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")
```

**Go**

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://cascades.crscreditapi.com/api/v1/executions/1842/notice_artifacts", 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))
}
```

**Agent**

```shell
claude 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 adverse-action notice pdfs for an execution 1842 using the Cascades API."
# No MCP tool covers this endpoint yet; the assistant can call GET /executions/{id}/notice_artifacts with curl.
```


## Where to go from here

- Replace polling with a [webhook subscription](/docs/concepts/webhooks) — `execution.completed` arrives seconds after the transition.
- Run many applicants at once with a [batch](/docs/concepts/batches).
- When your account is approved, flip `execution_mode` to `live`: [Sandbox vs live](/docs/concepts/sandbox-vs-live).

## Sitemap

See the full [sitemap](https://cascades.crscreditapi.com/sitemap.md) for all pages.
