# Retry a failed run

Queues a failed run of a deployed workflow to execute again.

- Endpoint: `POST https://api.returning.ai/v1/workflow-builder/data-workflows/runs/{runId}/retry`
- Section: Workflow Builder / Runs
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `workflowBuilder`
- Retries: This is the one call in the family whose whole point is a controlled repeat, so its own retry behaviour matters.
- Guide: generated from the published specification
- Last updated: 28 Oct 2026
- Web page: https://docs.returning.ai/api-reference/workflow-builder/retry-a-failed-run

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `workflowBuilder`

`Authorization: Bearer <API_KEY>` with `workflowBuilder`, and the run must belong to the key's community.

| Failure | Meaning | Recovery |
| --- | --- | --- |
| `403 API_KEY_PERMISSION_DENIED` | The key has no `workflowBuilder` permission | Grant the permission. |
| `404 RUN_NOT_FOUND` | Unknown run id, or a run of another community | Use a run id from the list. |
| `409 RETRY_IN_PROGRESS` | A retry of this run is already claimed, and this call carried no key or a different one | Wait for the queued retry to finish, then read the runs list. |

## Behaviour

A retry is not a resume: the whole workflow runs from the trigger again, including steps that already succeeded, paid rewards, or called an outside service.

There is no confirmation header and no separate retry permission to hold back a caller who should not retry: `workflowBuilder` covers it. What protects you is the per-run claim below, which stops two retries of one run from both queueing, and an `Idempotency-Key`, which turns a lost response into a replay.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `runId` | `string` | Yes | Run identifier: 24 hexadecimal characters. (`^[0-9a-fA-F]{24}$`) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key: `Bearer <API_KEY>`. |
| `Idempotency-Key` | `string` | No | Optional client-chosen key. The same key with the same body returns the original result instead of creating a second object. |

### Watch for

- A retry runs the whole workflow again. It does not continue from the failed step, so a step that already paid a reward runs again and may pay again.
- Only a failed run of a workflow that is currently deployed is queued. Anything else is skipped with `queued: 0` and a reason in `skipped`, including a run that succeeded, a run that is still in progress, and a workflow that is no longer deployed.
- A retry runs the workflow's **current deployed steps** with the failed run's **recorded trigger output**. It is the same event as far as the steps are concerned, against the configuration as it is now, so a step you changed after the failure runs in its new form.
- The response describes the request, not the result. Read the runs list for the outcome.
- There is no dry run of a retry: it is a real execution by definition, and the response says `sideEffects: "real"` on every answer. If you want to know what the retry will do, dry-run the workflow (`POST /{workflowId}/test` with `mode: "dry-run"`) before queueing it.

### Example request

```bash
curl --request POST \
  --url 'https://api.returning.ai/v1/workflow-builder/data-workflows/runs/<runId>/retry' \
  --header 'Authorization: Bearer <API_KEY>'
```

### More examples

```bash
curl --request POST \
  --url "https://api.returning.ai/v1/workflow-builder/data-workflows/runs/65f1a2b3c4d5e6f708192a45/retry" \
  --header "Authorization: Bearer <API_KEY>" \
  --header "Idempotency-Key: retry-65f1a2b3c4d5e6f708192a45-1"
```

A first call queues the run:

```json
{
  "data": {
    "requested": 1,
    "queued": 1,
    "skipped": [],
    "sideEffects": "real",
    "alreadyRequested": false,
    "reExecution": "The whole workflow runs again from the trigger, including steps that already succeeded, paid rewards or called outside services."
  }
}
```

A repeat with the same `Idempotency-Key` replays the recorded answer instead of queueing again:

```json
{
  "data": {
    "requested": 1,
    "queued": 1,
    "skipped": [],
    "sideEffects": "real",
    "alreadyRequested": true,
    "reExecution": "The whole workflow runs again from the trigger, including steps that already succeeded, paid rewards or called outside services."
  }
}
```

A second call while the first retry is still in progress, with a different key or no key, is refused:

```json
{
  "meta": {
    "status": "error",
    "statusCode": 409,
    "code": "RETRY_IN_PROGRESS",
    "runId": "65f1a2b3c4d5e6f708192a45",
    "requestId": "3f4d0e8b-7a6c-4f1e-9b2d-5a7c8e1f0a3b"
  },
  "message": "API request rejected error.",
  "detail": "A retry for this run is already in progress. Wait for it to finish before retrying again.",
  "solution": "Read error detail and try again."
}
```

## Response

HTTP `200` with `requested`, `queued`, `skipped`, `sideEffects`, `alreadyRequested` and `reExecution`.

- `queued: 1` with `alreadyRequested: false` means a retry was accepted. Read back with `GET /{workflowId}/runs`: a new run appears with its own id, and the original run keeps its result.
- `alreadyRequested: true` means the platform replayed the answer it recorded for this run and this `Idempotency-Key`. The body is the original answer, so `queued: 1` describes the first call rather than this one. A replay is served for 24 hours, whether or not the ten-minute claim is still held.
- `sideEffects: "real"` is always present. A retry is a real execution.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `data` | `object` | - | What the retry call accepted. |
| `data.requested` | `integer` | - | Runs the call asked to retry. |
| `data.queued` | `integer` | - | Runs actually queued by the call that produced this answer. |
| `data.skipped` | `object[]` | - | Runs that were not queued, with the reason. |
| `data.skipped.workflowHistoryId` | `string` | - | Run that was skipped. |
| `data.skipped.reason` | `string` | - | Why it was skipped. |
| `data.sideEffects` | `string` | - | Always `real`: a retry runs the workflow again. |
| `data.alreadyRequested` | `boolean` | - | True when this answer replays an earlier retry of the same run and `Idempotency-Key`. |
| `data.reExecution` | `string` | - | What a retry does, stated on every response. |

### Example response (200)

```json
{
  "data": {
    "requested": 1,
    "queued": 1,
    "skipped": [],
    "sideEffects": "real",
    "alreadyRequested": false,
    "reExecution": "The whole workflow runs again from the trigger, including steps that already succeeded, paid rewards or called outside services."
  }
}
```

## Errors

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `VALIDATION_FAILED` | The request is not valid. `meta.errors` names each field as `path` and `message`. The path says where the problem is: `params.` for a path parameter, `query.` for a query parameter, `body.` for a request field, and `body` itself for a body that is not valid JSON. An oversized body is `413` with the same code. |
| 401 | `AUTHENTICATION_REQUIRED` | No key, or an unknown key. Send a valid key. |
| 401 | `API_KEY_REQUIRED` | A valid admin session was used, because this API accepts community API keys only. Use a community API key. |
| 403 | `API_KEY_PERMISSION_DENIED` | The key has no `workflowBuilder` permission. Grant the permission. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `RUN_NOT_FOUND` | Unknown run id, or a run of another community. Use a run id from the list. |
| 409 | `RETRY_IN_PROGRESS` | A retry of this run is already claimed, and the call carried no key or a different key. Wait for the queued retry to finish, then read the runs list. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 429 | `RATE_LIMITED` | The community's API rate limit was reached. Wait for the window to reset, then retry. |
| 500 | `INTERNAL_ERROR` | Unexpected failure. Read the runs list before retrying: the retry may already be queued. |

**Retries:** This is the one call in the family whose whole point is a controlled repeat, so its own retry behaviour matters. Two guards run in this order: **A recorded answer for your key is checked first.** If this run already accepted a retry under your `Idempotency-Key`, the platform returns that answer with `alreadyRequested: true` and queues nothing. The record lasts 24 hours, so a client that retries after a timeout still cannot queue a second re-run, even long after the first claim expired. **Otherwise a claim is taken on the run and its current state.** While that claim is held, a call with no key or a different key is refused with `409 RETRY_IN_PROGRESS` and queues nothing. Two retries of one failed run can therefore never both queue. **The claim lasts ten minutes.** After it expires, a call with a new key or no key takes a new claim and queues a new re-run: the workflow really executes again, so a reward step can pay twice. Only the same key is protected, and only by the 24-hour record. If queueing itself fails, the claim is released, so a failed attempt does not block a legitimate retry. Use an `Idempotency-Key` on every retry: it is what turns a lost response into a replay instead of a refusal.

## Next step

- [Call the workflow tools over MCP](https://docs.returning.ai/api-reference/workflow-builder/call-the-workflow-tools-over-mcp.md): `POST /v1/workflow-builder/data-workflows/mcp`.
