---
title: Generate a cascade with AI
description: Turn a plain-English screening policy into a valid cascade with POST /workflows/generate, preview before saving, and handle the busy and failure cases.
canonical_url: "https://cascades.crscreditapi.com/docs/guides/ai-generate-a-cascade"
doc_version: "1.0.0"
last_updated: "2026-09-23T19:55:02Z"
source: docs/guides/ai-generate-a-cascade.md.erb
---

# Generate a cascade with AI

`POST /workflows/generate` takes a description of your screening policy and returns a cascade definition built by Claude, validated against the same node grammar the visual builder enforces. Use it to draft a cascade from a sentence, then refine in the builder or with `PATCH /workflows/{id}`.

## What do I send?

A `prompt` describing the policy in the language you would use with a colleague. Be explicit about products, thresholds, and what happens on each outcome:


**cURL**

```shell
curl -X POST "https://cascades.crscreditapi.com/api/v1/workflows/generate" \
  -H "Authorization: Bearer {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "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"
  }'
```

**Node.js**

```javascript
const response = await fetch("https://cascades.crscreditapi.com/api/v1/workflows/generate", {
  method: "POST",
  headers: {
    "Authorization": "Bearer {{API_KEY}}",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "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"
  })
});

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/generate",
    headers={
        "Authorization": "Bearer {{API_KEY}}",
    },
    json={
      "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"
    },
)
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/generate")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer {{API_KEY}}"
request["Content-Type"] = "application/json"
request.body = <<~JSON
  {
    "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"
  }
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(`{
	  "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"
	}`)
	req, err := http.NewRequest("POST", "https://cascades.crscreditapi.com/api/v1/workflows/generate", 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:
#   "Generate a workflow from a prompt and summarise the result."
# It will call the `cascades_generate_workflow` tool with your key.
```


Optional fields: `name` (otherwise the generator names it), `use_case` (defaults to `tenant_screening`), `active` (subject to the activation gate), and `create: false` to **preview** the definition without saving anything.

```json
{
  "prompt": "Verify identity, pull a TransUnion credit report and a criminal search. Approve FICO 680 and above with no felonies. Send 620–679 to manual review. Decline below 620 or any felony, with an adverse-action notice.",
  "create": false
}
```

## What comes back?

With `create: false`, `200` and a preview:

```json
{
  "data": {
    "object": "cascade_generation",
    "definition": [ { "id": "start", "type": "ApplicantSource", "next_step": ["identity"] }, "…" ],
    "valid": true,
    "name": "Tenant screening — FICO 680 gate",
    "model": "claude-opus-4-8",
    "repaired": false,
    "warnings": []
  },
  "meta": { "request_id": "…" }
}
```

By default the cascade is saved and the response is `201` with the workflow plus a `generation` block carrying the same `model`, `repaired` and `warnings` fields. `repaired: true` means the first draft failed validation and a cheaper model fixed it in one repair turn; read `warnings` — they list anything the generator could not express exactly (a product your account cannot use, an ambiguous threshold).

On a sandbox account the cascade is saved as a **draft** and the response carries a `note` saying so. It still runs in test mode.

## Which errors should I handle?

| Code | Meaning | What to do |
|---|---|---|
| `422 validation_failed` | Empty prompt, or longer than the cap | Trim the prompt |
| `422 generation_failed` | The model could not produce a valid definition; `details` lists the validation errors | Rephrase — name products and thresholds explicitly |
| `503 designer_busy` | The generator's concurrency cap is in use | Retry after a few seconds with backoff |
| `403 insufficient_scope` | Key lacks `workflows:write` | Use a key with the scope |

Generation is not idempotent: two identical prompts create two cascades. Use `create: false` when exploring.

## Iterating

The returned `definition` is ordinary JSON. Edit nodes, thresholds or edges and send the whole array back with `PATCH /workflows/{id}`; the same validator runs on every save. To test it, trigger it in test mode with a [registered subject](/docs/sandbox/test-identities) and read `step_outputs` to see which branch fired.


> **From an agent**
>
> The MCP server exposes this endpoint as `cascades_generate_workflow`, so in Claude Code or Cursor you can say *"Generate a cascade that pulls credit and eviction, approves FICO 660+ with no evictions, and declines the rest with a notice"* and then *"run it for a registered test subject"*. See [MCP server](/docs/mcp).


## Related

- [Cascades](/docs/concepts/cascades) — the node grammar the generator targets
- [`POST /workflows/generate`](/docs/api/workflows/generate-workflow)

## Sitemap

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