---
title: Approve or deny a paused review — POST /api/v1/executions/{id}/review_decisions
description: "Records the decision on an execution that is awaiting_review (or waiting) with needs_human_review: true, auto-claims it for the caller if unassigned, and…"
canonical_url: "https://cascades.crscreditapi.com/docs/api/executions/submit-review-decision"
doc_version: "1.0.0"
last_updated: "2026-10-09T18:04:24Z"
source: "public/api-docs/v1/swagger.yaml#submitReviewDecision"
---

# Approve or deny a paused review — POST /api/v1/executions/{id}/review_decisions

`POST https://cascades.crscreditapi.com/api/v1/executions/{id}/review_decisions`

- Operation id: `submitReviewDecision`
- Scopes: `executions:write`
- Idempotent: no
- OpenAPI: https://cascades.crscreditapi.com/openapi.json · https://cascades.crscreditapi.com/openapi.yaml (operationId `submitReviewDecision`)

Records the decision on an execution that is `awaiting_review` (or
`waiting`) with `needs_human_review: true`, auto-claims it for the
caller if unassigned, and resumes the chosen branch. Test-mode
executions resume inline and return `200` with the final state;
live-mode executions enqueue the resume and return `202` with
`resume_enqueued: true`. Requires `executions:write`.

## Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | integer | yes | Execution id. Scoped to the calling api_client; a foreign id returns 404. |

## Request body

`application/json` — `ReviewDecisionRequest`

| Field | Type | Required | Description |
|---|---|---|---|
| `decision` | string | yes |  One of: `"approved"`, `"denied"`. |
| `note` | string | no |  |

```json
{
  "decision": "approved",
  "note": "Manual review confirmed; OFAC clean."
}
```

## Responses

### 200 — Review decided and downstream branch ran inline (test mode)

`application/json`

| Field | Type | Required | Description |
|---|---|---|---|
| `data` | ExecutionDetail | no |  |
| &nbsp;&nbsp;`id` | integer | yes |  |
| &nbsp;&nbsp;`object` | string | yes |  |
| &nbsp;&nbsp;`workflow_id` | integer | yes |  |
| &nbsp;&nbsp;`status` | string | yes | Lifecycle of an execution. `queued` (batch rows not yet dispatched), `pending`, `running` and `waiting` (paused on a Wait node or adverse-action waiting period) are in flight; `awaiting_review` is paused on human review; `completed`, `failed`, `cancelled` and `skipped` are terminal. One of: `"queued"`, `"pending"`, `"running"`, `"waiting"`, `"awaiting_review"`, `"completed"`, `"failed"`, `"cancelled"`, `"skipped"`. |
| &nbsp;&nbsp;`execution_key` | string, nullable | no |  |
| &nbsp;&nbsp;`external_reference` | string, nullable | no |  |
| &nbsp;&nbsp;`needs_human_review` | boolean | yes |  |
| &nbsp;&nbsp;`review_status` | string | yes |  One of: `"none"`, `"in_review"`, `"approved"`, `"denied"`. |
| &nbsp;&nbsp;`assigned_to_user_id` | integer, nullable | no | The reviewer who claimed this execution, if any. |
| &nbsp;&nbsp;`started_at` | string (date-time), nullable | no |  |
| &nbsp;&nbsp;`completed_at` | string (date-time), nullable | no |  |
| &nbsp;&nbsp;`created_at` | string (date-time) | yes |  |
| &nbsp;&nbsp;`updated_at` | string (date-time), nullable | no |  |
| &nbsp;&nbsp;`input_context` | object | no | The payload originally sent at trigger time, plus resolved fields such as `execution_mode`, `submitted_by_user_id`, `user_approved`, `external_reference`, (for a test-mode run of a draft) `allow_inactive` and (for a test-mode run that replays a registered test subject) `sandbox_replay`. |
| &nbsp;&nbsp;`resume_enqueued` | boolean | no |  |
| `meta` | Meta | no |  |
| &nbsp;&nbsp;`request_id` | string | yes | Correlation id for this request. Also sent as the `X-Request-Id` response header; quote it when contacting support. |

```json
{
  "data": {
    "id": 1842,
    "object": "execution",
    "workflow_id": 27,
    "status": "queued",
    "execution_key": "api_client:12:3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "external_reference": "rv-app-20491",
    "needs_human_review": true,
    "review_status": "none",
    "assigned_to_user_id": 1,
    "started_at": "2026-05-08T18:41:05Z",
    "completed_at": "2026-05-08T18:41:05Z",
    "created_at": "2026-05-08T18:41:05Z",
    "updated_at": "2026-05-08T18:41:05Z",
    "input_context": {},
    "resume_enqueued": false
  },
  "meta": {
    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
  }
}
```

### 202 — Review decided and downstream branch enqueued (live mode)

`application/json`

| Field | Type | Required | Description |
|---|---|---|---|
| `data` | ExecutionDetail | no |  |
| &nbsp;&nbsp;`id` | integer | yes |  |
| &nbsp;&nbsp;`object` | string | yes |  |
| &nbsp;&nbsp;`workflow_id` | integer | yes |  |
| &nbsp;&nbsp;`status` | string | yes | Lifecycle of an execution. `queued` (batch rows not yet dispatched), `pending`, `running` and `waiting` (paused on a Wait node or adverse-action waiting period) are in flight; `awaiting_review` is paused on human review; `completed`, `failed`, `cancelled` and `skipped` are terminal. One of: `"queued"`, `"pending"`, `"running"`, `"waiting"`, `"awaiting_review"`, `"completed"`, `"failed"`, `"cancelled"`, `"skipped"`. |
| &nbsp;&nbsp;`execution_key` | string, nullable | no |  |
| &nbsp;&nbsp;`external_reference` | string, nullable | no |  |
| &nbsp;&nbsp;`needs_human_review` | boolean | yes |  |
| &nbsp;&nbsp;`review_status` | string | yes |  One of: `"none"`, `"in_review"`, `"approved"`, `"denied"`. |
| &nbsp;&nbsp;`assigned_to_user_id` | integer, nullable | no | The reviewer who claimed this execution, if any. |
| &nbsp;&nbsp;`started_at` | string (date-time), nullable | no |  |
| &nbsp;&nbsp;`completed_at` | string (date-time), nullable | no |  |
| &nbsp;&nbsp;`created_at` | string (date-time) | yes |  |
| &nbsp;&nbsp;`updated_at` | string (date-time), nullable | no |  |
| &nbsp;&nbsp;`input_context` | object | no | The payload originally sent at trigger time, plus resolved fields such as `execution_mode`, `submitted_by_user_id`, `user_approved`, `external_reference`, (for a test-mode run of a draft) `allow_inactive` and (for a test-mode run that replays a registered test subject) `sandbox_replay`. |
| &nbsp;&nbsp;`resume_enqueued` | boolean | no |  |
| `meta` | Meta | no |  |
| &nbsp;&nbsp;`request_id` | string | yes | Correlation id for this request. Also sent as the `X-Request-Id` response header; quote it when contacting support. |

```json
{
  "data": {
    "id": 1842,
    "object": "execution",
    "workflow_id": 27,
    "status": "queued",
    "execution_key": "api_client:12:3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "external_reference": "rv-app-20491",
    "needs_human_review": true,
    "review_status": "none",
    "assigned_to_user_id": 1,
    "started_at": "2026-05-08T18:41:05Z",
    "completed_at": "2026-05-08T18:41:05Z",
    "created_at": "2026-05-08T18:41:05Z",
    "updated_at": "2026-05-08T18:41:05Z",
    "input_context": {},
    "resume_enqueued": true
  },
  "meta": {
    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f"
  }
}
```

### 401 — Missing, malformed, expired or revoked bearer token. The `WWW-Authenticate` header carries the RFC 6750 challenge.

`application/json` — `ErrorEnvelope`

```json
{
  "error": {
    "code": "unauthorized",
    "message": "A valid bearer token is required.",
    "details": [],
    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
    "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/unauthorized"
  }
}
```

### 403 — `insufficient_scope`, `review_assigned_elsewhere` or `live_mode_forbidden`

`application/json` — `ErrorEnvelope`

```json
{
  "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.crscreditapi.com/docs/api/errors/review_assigned_elsewhere"
  }
}
```

### 404 — Resource does not exist or belongs to another tenant / api_client

`application/json` — `ErrorEnvelope`

```json
{
  "error": {
    "code": "not_found",
    "message": "The requested resource was not found.",
    "details": [],
    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
    "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/not_found"
  }
}
```

### 409 — `review_not_pending` or `review_already_decided`

`application/json` — `ErrorEnvelope`

```json
{
  "error": {
    "code": "review_not_pending",
    "message": "Execution is not awaiting human review.",
    "details": [],
    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
    "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/review_not_pending"
  }
}
```

### 422 — `validation_failed` — `decision` must be `approved` or `denied`

`application/json` — `ErrorEnvelope`

```json
{
  "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.crscreditapi.com/docs/api/errors/validation_failed"
  }
}
```

### 429 — Rate limit exceeded (60 req/min per IP on `/api/v1`, 600 req/hour
per token prefix, or 300 req/5 min per IP globally). The body is the
standard error envelope and `Retry-After` gives the seconds until
the window resets.


`application/json` — `ErrorEnvelope`

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry later.",
    "details": [],
    "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f",
    "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/rate_limited"
  }
}
```

## Code samples

### 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.
```

## Related

- [Human review](https://cascades.crscreditapi.com/docs/concepts/human-review)
- [Your first screening](https://cascades.crscreditapi.com/docs/guides/first-screening)
- [Handle human review](https://cascades.crscreditapi.com/docs/guides/handle-human-review)

## Sitemap

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