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

# Idempotency

> One key per message and destination

`POST /v1/messages` requires an `Idempotency-Key` header. Without one, you
get `400 missing_idempotency_key` before anything is written.

## Choosing a key

1 to 255 visible ASCII characters (`!` through `~`), no spaces. **The key
must be unique per send.** If one event of yours can go to several people or
conversations — a lead notification fanning out to a rep and a channel, say
— build the key from both:

```
<event_id>:<destination_id>
```

so `lead.created:usr_9a1…` and `lead.created:chn_c41…` are two different
keys for the same event, and a retry of either is safe.

## What a repeat does

A key is remembered for 24 hours after a successful send.

| You send | Within 24h | After 24h |
| - | - | - |
| Same key, same body | Original `201`, nothing posted again | Original `201` if the message is still unchanged |
| Same key, different body | `422 idempotency_key_reused` | `422 idempotency_key_reused` |
| Same key, message since edited or deleted through the API | — | `422 idempotency_key_reused` |

After 24 hours the stored response itself is gone, but the key still can't
post a second message to the same destination — Workchats checks the
message it already created there, not just the 24-hour cache. That's why
the table above still has an answer after 24 hours: a key is permanently
tied to at most one message per destination.

If somehow a different message already exists under your key's identity in
that destination (this shouldn't happen from your side, but a client
library bug elsewhere could cause it), you get `422 idempotency_key_reused`
and nothing is posted — you never get answered as if it were yours.

## Why this matters

Retry on timeouts and `5xx` freely. Without a correct, unique key, a retried
send after a lost response can double-post. With one, retrying is always
safe.

A non-JSON request body is `415 unsupported_media_type`, independent of the
idempotency key.
