# Idempotency

> How to repeat a call that sends messages without sending twice. The Idempotency-Key header, how long it is kept and what happens on a repeat.

Last updated: 2026-10-30. Source: https://docs.smartchat.marketing/en/concepts/idempotency/

A network can fail after the server did the work but before you saw the answer. If you simply send the request again, a message could go out twice. The header `Idempotency-Key` prevents this.

## How to use it

Send a unique value for each **operation**, for example a UUID. Use the same value when you repeat that operation. This example creates a contact; calls that send messages use the header in the same way.

```bash run
curl -X POST "https://api.smartchat.marketing/v1/contacts" \
  -H "Authorization: Bearer $SMARTCHAT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name": "Mia Muster", "phone": "+4915112345678"}'
```

- The key has 1 to 255 visible ASCII characters.
- It is **required** on every call that sends messages and on every call that creates something with an outside effect. The reference page of a call says "Required". Without it the answer is [`idempotency_key_required`](https://docs.smartchat.marketing/en/errors/#idempotency_key_required).
- It is optional on some other calls, and it works if you send it.

## What happens on a repeat

| You send | You get |
|---|---|
| The same key and the same request | The first answer again, with the header `Idempotent-Replayed: true`. Nothing is done twice. |
| The same key and a different request | 422 [`idempotency_key_reused`](https://docs.smartchat.marketing/en/errors/#idempotency_key_reused). Use a new key. |
| The same key while the first request still runs | 409 [`idempotency_in_progress`](https://docs.smartchat.marketing/en/errors/#idempotency_in_progress) with `Retry-After`. Wait and repeat. |

Keys are kept for 24 hours. They belong to your key and your account.

## What is not stored

Answers with 401, 403, 409, 429 and 503 are not stored. Nothing was done, or the state can change, so the key is free again.

Some 422 refusals depend on the state of your account. Nothing was sent, and the state can change (a template gets approved, a window opens, a contact confirms). These refusals are not stored either, so you can repeat the **same** request with the **same** key once the state has changed:

- [`audience_empty`](https://docs.smartchat.marketing/en/errors/#audience_empty)
- [`contact_not_confirmed`](https://docs.smartchat.marketing/en/errors/#contact_not_confirmed)
- [`quota_exceeded`](https://docs.smartchat.marketing/en/errors/#quota_exceeded)
- [`template_not_approved`](https://docs.smartchat.marketing/en/errors/#template_not_approved)
- [`template_rejected`](https://docs.smartchat.marketing/en/errors/#template_rejected)
- [`template_variables_missing`](https://docs.smartchat.marketing/en/errors/#template_variables_missing)
- [`test_phone_not_set`](https://docs.smartchat.marketing/en/errors/#test_phone_not_set)
- [`test_phone_unverified`](https://docs.smartchat.marketing/en/errors/#test_phone_unverified)
- [`whatsapp_not_connected`](https://docs.smartchat.marketing/en/errors/#whatsapp_not_connected)
- [`window_closed`](https://docs.smartchat.marketing/en/errors/#window_closed)

Other errors are stored, in particular errors that come from a service behind us (for example WhatsApp), because it is not clear if the message was sent. A repeat with the same key then gives the same answer and never a second send.

## Calls that take no key on purpose

`create_webhook_endpoint` and `rotate_webhook_secret` take no `Idempotency-Key`. Both answers contain a signing secret, and a secret is never kept in the replay storage. A repeated call therefore creates a second endpoint (the endpoint limit applies) or rotates the secret again. If you did not see the answer, list your endpoints first and unsubscribe the extra one (`unsubscribe_webhook_endpoint`) or reuse it.

> **Important** If a call ends with [`upstream_error`](https://docs.smartchat.marketing/en/errors/#upstream_error), do not repeat the request with the same key: you would only get the same answer again. First read the status (for a send: read the send, [`get_send`](https://docs.smartchat.marketing/en/api/reference/sends/get_send/)). If the message was not sent, use a **new** key. If it was sent, do nothing.

## The key in MCP tools

The MCP tools take the same key as the argument `idempotency_key`. The rule is the same as in REST, with one help for agents:

| The tool | Without a key |
|---|---|
| Sends a message | Refused with [`idempotency_key_required`](https://docs.smartchat.marketing/en/errors/#idempotency_key_required), as in REST. Send a key. |
| Creates something (for example a contact or a draft) | The server makes a key from the tool name and the arguments. It is valid for **one minute**. The same call within that minute returns the first answer. Send your own key to get the full 24 hours. |
| Changes something that already exists (set a status, add a tag) | No key is made. A repeat does the change again, because a change can be wanted twice. |

## Required, optional, not needed

Every reference page shows exactly one of three words for the `Idempotency-Key`:

| Word | Meaning |
|---|---|
| Required | Without the key the call answers [`idempotency_key_required`](https://docs.smartchat.marketing/en/errors/#idempotency_key_required). |
| Optional | The call works without it. If you send a key, a repeat returns the first answer. |
| Not needed | The call does not ask for a key. |

The reference page also says in the row "Safe to repeat" whether a repeat is harmless by itself. This is a separate fact. A call that changes a setting or reads data can be repeated without a key because the end state is the same. A call that creates something or does work each time is not safe to repeat without a key.

