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

# Idempotency

> Safely retry requests without performing the same operation twice.

All `POST`, `PATCH`, and `DELETE` requests accept an `Idempotency-Key` header. Retry with the same key and Deck returns the original response instead of performing the operation again. `GET` requests are naturally idempotent and do not require a key.

## How it works

Include an `Idempotency-Key` header on any `POST`, `PATCH`, or `DELETE` request.

```bash theme={null}
curl -X POST https://api.deck.co/v2/agents \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{ "name": "Hotel Reservations" }'
```

When Deck receives a request with an idempotency key and the request succeeds, it caches the response and associates it with that key. Only successful responses are cached. If the original request returned an error, retrying with the same key will execute the request again. Keys are scoped to your organization, so different organizations can use the same key independently.

If the same key is sent again:

* Same parameters: Deck returns the cached response. The operation is not repeated.
* Different parameters: Deck returns a `409 Conflict` with an `idempotency_error` to prevent accidental misuse.
* After 24 hours: the key expires and can be reused.

## When to use them

Use idempotency keys on any create operation where a duplicate would cause problems:

| Operation                       | Why                                              |
| ------------------------------- | ------------------------------------------------ |
| Storing a credential            | Prevents duplicate credentials for the same user |
| Running a task                  | Prevents the same task from executing twice      |
| Creating agents, sources, tasks | Prevents duplicate resources                     |
| Creating event destinations     | Prevents duplicate webhook subscriptions         |
| Updating or deleting resources  | Guarantees the operation happens exactly once    |

You don't need idempotency keys for `GET` requests. Reads are naturally idempotent.

## Key format

Keys can be any string up to 256 characters. Keys longer than 256 characters are rejected with a `400` error.

```text theme={null}
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
```

Common patterns:

| Pattern            | Example                                | When to use                                  |
| ------------------ | -------------------------------------- | -------------------------------------------- |
| Business logic key | `user_456_run_task_789`                | When the operation maps to a domain action   |
| Request identifier | `req_20260218_abc123`                  | When you track requests in your system       |
| UUID               | `550e8400-e29b-41d4-a716-446655440000` | When you need a guaranteed-unique random key |

## Generating keys

<CodeGroup>
  ```javascript Node.js (UUID) theme={null}
  const { randomUUID } = require('crypto')
  const idempotencyKey = randomUUID()
  ```

  ```javascript Node.js (business key) theme={null}
  const idempotencyKey = `user_${userId}_run_${taskId}`
  ```

  ```python Python (UUID) theme={null}
  import uuid
  idempotency_key = str(uuid.uuid4())
  ```

  ```python Python (business key) theme={null}
  idempotency_key = f"user_{user_id}_run_{task_id}"
  ```
</CodeGroup>

## Retry pattern

A typical retry flow with idempotency:

```javascript theme={null}
async function createAgent(name, retries = 3) {
  const idempotencyKey = `create_agent_${name}`

  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      const res = await fetch('https://api.deck.co/v2/agents', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.DECK_API_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': idempotencyKey,
        },
        body: JSON.stringify({ name }),
      })

      return await res.json()
    } catch (err) {
      if (attempt === retries) throw err
      await new Promise(r => setTimeout(r, 1000 * attempt))
    }
  }
}
```

The key stays the same across retries. If the first request succeeded but the response was lost, the second request returns the cached result. If the first request failed or never reached the server, the second one executes normally since only successful responses are cached.

## Error handling

**Key reuse with different parameters** returns HTTP `409`:

```json theme={null}
{
  "errors": [
    {
      "type": "idempotency",
      "code": "idempotency_error",
      "message": "Idempotency key has already been used with different parameters."
    }
  ],
  "request_id": "req_a1b2c3d4..."
}
```

If you see this error, use a new key for the new request.

**Key exceeds 256 characters** returns HTTP `400`:

```json theme={null}
{
  "errors": [
    {
      "type": "idempotency",
      "code": "idempotency_key_error",
      "message": "Idempotency key must be at most 256 characters"
    }
  ],
  "request_id": "req_a1b2c3d4..."
}
```
