Pular para o conteúdo principal

Idempotência

A API suporta idempotência para repetir solicitações com segurança, sem executar a mesma operação duas vezes. Útil quando uma chamada é interrompida em trânsito e você não recebe resposta — por exemplo, uma requisição para criar uma assinatura que cai por erro de rede pode ser repetida com a mesma chave, garantindo que apenas uma assinatura seja criada.

Como usar

Envie o cabeçalho X-Idempotency-Key: <chave> em qualquer requisição POST, PUT ou PATCH. A chave é livre, gerada pelo seu lado. Recomendamos UUID v4 ou outra string aleatória com entropia suficiente para evitar colisões.

curl -i \
-H "Authorization: Bearer $KOBANA_TOKEN" \
-H 'Content-Type: application/json' \
-H 'X-Idempotency-Key: 8c0f5d6e-3f8b-4cb5-9a47-d8f5b15e9b21' \
-H 'User-Agent: Kevin Mitnick <kmitnick@kobana.com.br>' \
-d '{
"subscription": {
"billing_account_id": "ba_01HXY123",
"plan_id": "plan_01HPRO",
"billing_cycle": "monthly"
}
}' \
-X POST 'https://api.billing.sandbox.kobana.com.br/v1/subscriptions'

Como funciona

Quando o servidor recebe uma requisição com X-Idempotency-Key:

  1. Reserva a chave atomicamente, escopada à sua organização. Duas organizações diferentes podem usar a mesma chave sem conflito.
  2. Calcula a impressão digital da requisição — SHA-256 de método + caminho + corpo bruto. Esse hash é guardado junto com a chave.
  3. Executa o handler. Se ele retornar uma resposta, o servidor grava status_code, corpo e cabeçalhos relevantes vinculados à chave.
  4. Marca a chave como completa. Qualquer nova requisição com a mesma chave dentro de 24 horas recebe a resposta original verbatim — mesmo status_code, mesmo corpo, mesmos cabeçalhos — acrescida do cabeçalho X-Idempotent-Replay: true para indicar que se trata de uma reexecução.

A camada salva o resultado independentemente de ter sido sucesso ou falha. Retries com a mesma chave retornam o mesmo resultado, incluindo erros 5xx.

Limites

  • Tamanho máximo da chave: 255 caracteres.
  • Retenção: chaves expiram automaticamente 24 horas após a criação. Após expirar, uma nova solicitação com a mesma chave gera um novo resultado.
  • Escopo: por organização. Uma chave usada em uma organização não interfere em outra.
  • Caracteres aceitos: qualquer string UTF-8 imprimível.

Quando o resultado não é salvo

A camada só grava o resultado quando o handler começou a executar. Não há registro nos seguintes casos — é seguro reenviar a requisição:

  • A chave está vazia ou passou de 255 caracteres → resposta 400 bad_request.
  • O parser do envelope da API rejeita a requisição antes do handler (por exemplo, JSON inválido ou autenticação ausente).
  • O handler lança uma exceção durante a execução — a reserva é liberada para que o próximo retry possa rodar limpo.
  • Outra requisição com a mesma chave está em execução simultânea → resposta 409 conflict com details.reason = "idempotency_request_in_progress".

Conflitos

Se a mesma chave for reutilizada com uma requisição diferente (qualquer mudança em método, caminho ou corpo), a API responde 409 conflict com details.reason = "idempotency_key_reused". Isso protege contra reuso acidental — por exemplo, persistir a chave junto com a operação no seu lado e tentar criar dois recursos diferentes com o mesmo identificador.

Resposta de erro

{
"error": {
"code": "conflict",
"message": "X-Idempotency-Key was already used with different request parameters. Reuse the original parameters or generate a new key.",
"details": { "reason": "idempotency_key_reused" }
}
}

Outras respostas possíveis

CenárioStatuserror.codedetails.reason
Outra requisição com a mesma chave em execução409conflictidempotency_request_in_progress
Mesma chave reutilizada com payload diferente409conflictidempotency_key_reused
Chave vazia ou maior que 255 caracteres400bad_requestinvalid_idempotency_key

Métodos aceitos

MétodoAceita X-Idempotency-Key?
POST✅ Sim
PUT✅ Sim
PATCH✅ Sim
GET❌ Não (já é idempotente por definição)
DELETE❌ Não (já é idempotente por definição)

Enviar a chave em GET ou DELETE não tem efeito — a API processa a requisição normalmente, sem gravar registro.

Identificando uma reexecução

Toda resposta servida a partir do registro carrega o cabeçalho:

X-Idempotent-Replay: true

Você pode usar esse cabeçalho para distinguir, no seu lado, entre a primeira execução e um retry replicado pelo servidor.

Boas práticas

  • Uma chave por operação lógica. Não reutilize a mesma chave para criar dois recursos diferentes — você receberá 409 conflict.
  • Persista a chave junto com o estado da operação no seu lado, para que o retry depois de crash ou reinício use a mesma chave.
  • Combine com backoff exponencial ao receber 5xx ou timeout — a chave garante que o retry não duplica o recurso. Como o resultado é gravado também para falhas, você pode parar de tentar assim que vier o status code esperado.
  • Limpe a chave depois de 24 horas. Não há ganho em mantê-la no seu lado após esse prazo: o servidor já descartou o registro.
  • Não envie a chave em métodos que não a aceitam. Em GET/DELETE ela é silenciosamente ignorada — preferimos que o cliente nem a inclua para deixar claro o que é idempotente por contrato e o que depende da chave.