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

# Idempotency keys

> Send an Idempotency-Key with every write so a retried request never books twice.

A network error after you send `POST /v1/appointments` leaves you unsure whether the appointment exists. With an idempotency key you can simply send the same request again: Brelio returns the first result instead of booking twice.

## The rule

Every write (`POST` and `PATCH`) needs an `Idempotency-Key` header. Reads (`GET`) do not.

```bash theme={null}
curl -X POST https://api.brelio.app/v1/appointments \
  -H "Authorization: Bearer $BRELIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0f7e3c52-3b1a-4d55-9a7e-6f2b1c8d9e10" \
  -d '{"patientId":"…","professionalId":"…","serviceId":"…","start":"2026-11-16T08:00:00Z"}'
```

* **Format:** 8 to 255 printable ASCII characters without spaces. A UUID v4 is the simplest choice.
* **One key per intended change.** Generate it before the first attempt and keep it for every retry of that same change.
* **Scope:** per credential and operation. Two credentials never collide.
* **Lifetime:** Brelio remembers a key for 24 hours.

## What happens

| You send | Brelio answers |
| - | - |
| A new key | Runs the operation and stores the result. |
| The same key and the same body again | The stored result, with the same status (for example `201`), without running anything. |
| The same key with a different body | `409 OPERATION_IDEMPOTENCY_CONFLICT`. Nothing runs. |
| The same key while the first request is still running | `409 OPERATION_IDEMPOTENCY_IN_PROGRESS`. Wait a moment and retry. |
| No key on a write | `400 IDEMPOTENCY_KEY_REQUIRED`. |

```json 409 OPERATION_IDEMPOTENCY_CONFLICT theme={null}
{
  "type": "https://api.brelio.app/problems/operation-idempotency-conflict",
  "title": "Request refused",
  "status": 409,
  "code": "OPERATION_IDEMPOTENCY_CONFLICT",
  "detail": "The idempotency key was already used with a different request for appointments.create",
  "request_id": "17eb0fd0-c750-4dfa-bf6a-654f32dbb877",
  "params": { "operationId": "appointments.create" }
}
```

## Secrets are shown once

Some writes return a secret once (a webhook's signing `secret`). A replay of that request returns the same record **without** the secret and the header `Brelio-Secret-Already-Shown: true`. Store the secret from the first answer.

## Retry safely

```javascript Node.js theme={null}
async function createWithRetry(path, body) {
  const key = crypto.randomUUID() // once, before the first attempt
  for (let attempt = 0; attempt < 4; attempt++) {
    try {
      const response = await fetch(`https://api.brelio.app/v1${path}`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.BRELIO_API_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': key,
        },
        body: JSON.stringify(body),
      })
      if (response.status < 500 && response.status !== 429) return response
    } catch {
      // network error: retry with the same key
    }
    await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000))
  }
  throw new Error('Brelio did not answer')
}
```


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