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

# Idempotência

> Repita uma escrita com segurança: a mesma chave devolve a mesma resposta, nunca uma segunda execução.

Seu backend é o único chamador que repete sozinho. Um cliente HTTP estoura o
timeout e tenta de novo, uma fila reentrega, um deploy replica um lote que ficou
pela metade. A requisição chega duas vezes porque o transporte está fazendo o
trabalho dele.

Toda escrita na superfície da chave secreta aceita o header `Idempotency-Key`.
Envie um, e a repetição recebe **a mesma resposta de volta** — mesmo status,
mesmo corpo — em vez de executar outra vez.

```bash theme={null}
curl -s $API/v1/contacts \
  -H "Authorization: Bearer $UK_SECRET_KEY" \
  -H "Idempotency-Key: 2f1c8a3e-9b40-4c2d-8f11-6a7e0d5b2c94" \
  -H 'Content-Type: application/json' \
  -d '{ "external_id": "user_8421", "email": "ada@example.com" }'
```

```json 201 theme={null}
{ "contact": { "id": "9d1f…", "email": "ada@example.com" }, "created": true }
```

Envie de novo, byte a byte, e você recebe o mesmo `201` com `created: true`:

```
HTTP/1.1 201 Created
Idempotent-Replay: true
```

Sem a chave, essa segunda chamada responde `200` com `created: false` — correto,
e inútil para o código que a fez. Um programa que provisiona um workspace quando
`created` é verdadeiro não consegue distinguir "outra pessoa já fez isso" de "a
minha própria repetição passou".

## Escolhendo uma chave

Qualquer valor opaco, até 255 caracteres. Um UUID por tentativa é a resposta
usual; o seu próprio identificador da operação (`order_8421-refund`) também
funciona, e tem a vantagem de que um processo que recomeça do zero gera o mesmo.

A chave é escopada pela **sua chave de API e pela rota**. Dois inquilinos
enviando `Idempotency-Key: 1` nunca colidem, uma chave live nunca repete o que
uma chave test respondeu, e a mesma chave em outro endpoint é outra operação.

As chaves são lembradas por **24 horas**. Toda repetição que algum dia vai
carregar uma delas acontece dentro dessa janela — o backoff de um cliente é de
segundos, a reentrega de uma fila é de minutos, uma pessoa reexecutando um job
que falhou é de horas. Um dia depois você não está repetindo, está chamando de
novo, e deve enviar uma chave nova.

## As três respostas que uma repetição pode receber

<AccordionGroup>
  <Accordion title="Repetida — a mesma resposta">
    A primeira requisição terminou. Você recebe o status e o corpo dela, mais
    `Idempotent-Replay: true`. O handler não roda.
  </Accordion>

  <Accordion title="409 — ainda em execução">
    Outra requisição com essa chave está em voo. Exatamente uma delas chega ao
    handler, por construção — então isso não é uma corrida que você perdeu, é a
    corrida sendo fechada. Espere o `Retry-After` e tente de novo; você vai
    receber a resposta repetida.

    ```json 409 theme={null}
    {
      "error": {
        "code": "idempotency_key_in_flight",
        "message": "a request with this Idempotency-Key is still running; retry in a moment"
      }
    }
    ```
  </Accordion>

  <Accordion title="422 — a chave foi usada para outra coisa">
    A chave já está registrada com outro corpo ou outra query string. Repetir a
    primeira resposta aqui diria que uma chamada deu certo quando ela nunca
    aconteceu, então ela é recusada.

    ```json 422 theme={null}
    {
      "error": {
        "code": "idempotency_key_reused",
        "message": "this Idempotency-Key was already used for a different request; use a new key"
      }
    }
    ```

    Isso quase sempre significa uma chave reaproveitada sem querer — uma
    constante esquecida no código, ou uma repetição que remontou o payload a
    partir de um estado que mudou. Gere uma chave por operação, não por processo.
  </Accordion>
</AccordionGroup>

## Falha não é resultado

Uma requisição que respondeu `5xx`, ou que foi barrada pelo limite de taxa antes
de chegar ao handler, não deixou nada para trás. A chave dela volta para a
prateleira: repita com a **mesma chave** e a requisição executa.

Uma requisição que respondeu `201` criou algo, e a chave dela está gasta. A que
respondeu `400` também — o mesmo corpo vai produzir o mesmo `400`, e repeti-lo
não custa nada a você.

Essa assimetria é a regra inteira, e é o que torna correto o laço de repetição
ingênuo:

<Steps>
  <Step title="Gere uma chave para a operação">
    Antes da primeira tentativa, não dentro dela.
  </Step>

  <Step title="Envie em todas as tentativas">
    Mesma chave, mesmo corpo.
  </Step>

  <Step title="Trate 409 como 'ainda não'">
    Faça backoff e tente de novo. Trate `422` como um bug do seu próprio código —
    uma chave nova não conserta uma chave reaproveitada, esconde.
  </Step>
</Steps>

## Onde vale

Só na superfície da chave secreta, `uk_sk_…`. Leituras ignoram o header: repetir
um `GET` devolveria uma resposta velha para uma pergunta que você fez justamente
porque queria uma nova.

As superfícies chamadas do navegador — `/v1/boot`, `/v1/contact-auth/*`,
`/v1/contact/*` — não aceitam o header. Elas são dirigidas por uma pessoa, e os
fluxos que poderiam duplicar já são de uso único por construção: um magic link,
um código por e-mail e um callback social são gastos pela primeira requisição que
os resgata.

<Note>
  `POST /v1/contacts` resolve para a mesma pessoa, seja chamado uma ou dez vezes —
  valores identificadores nunca reapontam para um segundo contato. A chave é o que
  torna a *resposta* estável também, e é nela que o seu código de fato ramifica.
</Note>
