> ## Documentation Index
> Fetch the complete documentation index at: https://docs.api.rask.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency and retries

> Retry Rask API v3 requests without charging twice: how Idempotency-Key works, which operations require it, and what each retry answer means.

Networks fail after the work started, so a client cannot always tell whether a request went through. An `Idempotency-Key` makes the retry safe: the same key and the same request never run twice.

```typescript TypeScript theme={null}
const { data: job } = await rask.dubbing.createDub({
  path: { project_id: projectId },
  body: { mode: 'auto', expected_cost_minutes: 1 },
  idempotencyKey: '8e03978e-40d5-43e8-bc93-6894a57f9324',
});
```

```python Python theme={null}
job = client.dubbing.create_dub(
    project_id, mode="auto", expected_cost_minutes=1, idempotency_key="8e03978e-40d5-43e8-bc93-6894a57f9324"
)
```

```go Go theme={null}
job, err := client.Dubbing.CreateDub(ctx, projectID, api.CreateDubRequest{
	Mode:                rask.Ptr(api.DubModeAuto),
	ExpectedCostMinutes: 1,
}, rask.WithIdempotencyKey("8e03978e-40d5-43e8-bc93-6894a57f9324"))
if err != nil {
	return err
}
```

```java Java theme={null}
Job job = rask.dubbing().createDub(
        projectId,
        CreateDubRequest.builder().mode(DubMode.AUTO).expectedCostMinutes(1).build(),
        RequestOptions.builder().idempotencyKey("8e03978e-40d5-43e8-bc93-6894a57f9324").build()).data();
```

```php PHP theme={null}
$job = $rask->dubbing->createDub(
    $projectId,
    ['mode' => 'auto', 'expected_cost_minutes' => 1],
    ['idempotency_key' => '8e03978e-40d5-43e8-bc93-6894a57f9324'],
);
```

```bash cURL theme={null}
curl -X POST "https://api.rask.ai/v3/projects/$PROJECT_ID/dub" \
  -H "Authorization: Bearer $RASK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8e03978e-40d5-43e8-bc93-6894a57f9324" \
  -d '{"mode": "auto", "expected_cost_minutes": 1}'
```

Generate one UUIDv4 per logical request, store it before you send, and send the same value on every retry of that request.

## The rules

* **Accepted** on every POST, PATCH and PUT, and on [deleteSegment](https://docs.api.rask.ai/operations/deleteSegment.md): 1 to 255 printable ASCII characters. Any other value answers 422 [`validation_failed`](https://docs.api.rask.ai/errors/validation_failed).
* **Required** on every operation that starts a job or spends credits, and on [createSegment](https://docs.api.rask.ai/operations/createSegment.md) and [restoreTranscriptVersion](https://docs.api.rask.ai/operations/restoreTranscriptVersion.md). Without it they answer 400
  [`idempotency_key_required`](https://docs.api.rask.ai/errors/idempotency_key_required). The [operation table](https://docs.api.rask.ai/v3/concepts/operation-table.md) marks them.
* **Ignored** on estimates, whatever its value: every estimate is computed fresh.
* **Kept 24 hours,** per workspace and acting user, so it survives replacing the API key.

## What a retry answers

| Situation | Answer |
| - | - |
| The same key and the same request, after success | The stored response with `Idempotent-Replayed: true`. Links are freshly signed, and a request that started a job shows the job's **current** state |
| The same key with a different request | 422 [`idempotency_key_reused`](https://docs.api.rask.ai/errors/idempotency_key_reused). The operation, path, query and body all count, `expected_cost_minutes` included, while the order of JSON keys and query parameters does not: retry a request unchanged, and give a changed request a new key |
| The first request is still in flight | 409 [`idempotency_key_in_use`](https://docs.api.rask.ai/errors/idempotency_key_in_use). Wait `Retry-After` and send it again. An attempt that never answered holds the key for about a minute at most |
| The first attempt answered a 4xx error, such as 402, 403, 409, 412, 422 or 429 | Nothing is kept under the key, and the next request with it runs from the start |
| The first attempt ended with a server error | The retry runs the request again. A request that starts a job keeps the job id of the first attempt, so nothing is charged twice |
| The job started by this key failed or was canceled | The same key replays that job. A new attempt needs a **new** key |

## When to retry

The problem says it in `retry`:

| `retry` | What to do |
| - | - |
| `same_key` | Send the same request with the same key, after `Retry-After` when given. Every 429 and 503 says this, as do a 500 [`internal_error`](https://docs.api.rask.ai/errors/internal_error), the 409s that ask you to wait, and [`upload_incomplete`](https://docs.api.rask.ai/errors/upload_incomplete) once the missing parts are uploaded |
| `new_key` | Start again with a new key. A failed job's `error` says this, or `none` |
| `none` | Do not send it unchanged. `recommendation` says what has to happen first |

Before a new attempt at paid work, call the estimate again, because the price may have changed. A higher price is a new
decision for whoever accepted the first one, so do not send it as the price cap on your own.

A client that may restart keeps its keys where a restart finds them. The [Quickstart's client](https://docs.api.rask.ai/v3/quickstart.md#the-same-in-python)
saves them in a file with the price it accepted, so a rerun sends the same requests and the API replays them.

## Edits and deletes

An edit that requires `If-Match` is protected twice. Its stale ETag makes a blind retry fail with 412, and the replay check runs **before** the `If-Match` check, so a retry of a successful edit with its key replays instead of failing. Send a key on those too.

A DELETE takes no key, and repeating it answers 204 for at least 30 days after the delete. The one exception is [deleteSegment](https://docs.api.rask.ai/operations/deleteSegment.md), a hard delete that returns an edit result: it accepts a key, and without one a repeat answers 404 [`segment_not_found`](https://docs.api.rask.ai/errors/segment_not_found).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.