Pular para o conteúdo

Idempotência

Como funciona

Escolha uma chave única por operação (um UUID, ou um ID seu, como um número de pedido) e envie como Idempotency-Key. Por 24 horas, a mesma chave na mesma rota devolve de novo o primeiro resultado, com Idempotent-Replayed: true, em vez de refazer o trabalho.

  • A mesma chave com um corpo diferente responde 422 idempotency_conflict.
  • A mesma chave enquanto a primeira requisição ainda roda responde 409 conflict: espere e tente de novo.
  • Uma requisição que falhou não é guardada, então tentar de novo com a mesma chave faz o trabalho.
  • As chaves valem para o espaço de trabalho, quem chama, a rota e o caminho dela. Quem chama é a chave de API (ou, no app, o membro conectado): duas chaves de API que enviam o mesmo Idempotency-Key nunca reproduzem as respostas uma da outra, e uma chave de API girada é um novo chamador. A mesma chave nas rotas de dois códigos diferentes também não colide.
  • PATCH e DELETE não aceitam chave: repeti-los deixa o mesmo estado.

As repetições nunca levam um segredo

A resposta guardada nunca inclui um segredo. Um POST que devolve um (registrar um endpoint de webhook, e criar ou girar uma chave de API no app) leva o segredo só na primeira resposta, com secretAlreadyShown: false. Uma repetição dentro das 24 horas responde o mesmo objeto com secret: null e secretAlreadyShown: true.

Se a primeira resposta se perdeu, o objeto ainda existe (o ID dele está na repetição). Gire o segredo do endpoint no app (ou apague o endpoint e registre de novo), ou gire a chave de API, para obter um novo segredo.

Repetição de POST /v1/webhooks (trecho)
{  "id": "whe_3k8m2q9x7c4v1b6n",  "url": "https://hooks.example.com/dotwick",  "secret": null,  "secretAlreadyShown": true}

Tentando de novo com segurança

exemploPOST /v1/codes
import { randomUUID } from "node:crypto";// one key per logical operation, reused on every retry of itconst idempotencyKey = randomUUID();for (let attempt = 1; attempt <= 5; attempt++) {  const res = await fetch("https://api.dotwick.com/v1/codes", {    method: "POST",    headers: {      Authorization: `Bearer ${process.env.DOTWICK_API_KEY}`,      "Content-Type": "application/json",      "Idempotency-Key": idempotencyKey,    },    body: JSON.stringify({ name: "Spring menu", content: { type: "url", url: "https://example.com/menu" } }),  });  if (res.ok) break; // a replay answers the first result with Idempotent-Replayed: true  if (res.status !== 429 && res.status < 500) throw new Error((await res.json()).error.message);  const wait = Number(res.headers.get("Retry-After") ?? 2 ** attempt);  await new Promise((r) => setTimeout(r, wait * 1000));}

Rotas que aceitam uma chave

Todo POST exceto o upload multipart POST /v1/assets.

MétodoCaminhoEscopoFinalidade
POST/v1/codescodes:writeCria um códigoidempotente
POST/v1/codes/movecodes:writeMove códigos para uma pastaidempotente
POST/v1/codes/move-domaincodes:writeMove códigos para outro domínioidempotente
POST/v1/codes/tagscodes:writeAdiciona ou remove tags em vários códigosidempotente
POST/v1/codes/{id}/duplicatecodes:writeDuplica um códigoidempotente
POST/v1/codes/bulkcodes:writeCria até 500 códigos em uma única execuçãoidempotente
POST/v1/bulk-imports/{id}/cancelcodes:writeCancela uma execução na fila ou em andamentoidempotente
POST/v1/bulk-imports/{id}/retrycodes:writeTenta de novo as linhas com falha de uma execuçãoidempotente
POST/v1/analytics/exportsanalytics:readEscaneamentos em CSV, preparados em segundo planoidempotente
POST/v1/reportsanalytics:readGera um relatório de estatísticas em PDF ou CSVidempotente
POST/v1/exportscodes:readExporta vários códigos em um único ZIPidempotente
POST/v1/tagstags:writeCria uma tagidempotente
POST/v1/foldersfolders:writeCria uma pastaidempotente
POST/v1/templatestemplates:writeCria um modeloidempotente
POST/v1/webhookswebhooks:writeRegistra um endpoint e os tipos de eventoidempotente
POST/v1/webhooks/{id}/testwebhooks:writeEnvia um evento webhook.testidempotente
POST/v1/webhooks/{id}/deliveries/{deliveryId}/redeliverwebhooks:writeEnvia uma entrega de novoidempotente
POST/v1/webhooks/{id}/recoverwebhooks:writeReenvia as entregas com falhaidempotente
POST/v1/hookswebhooks:writeInscreve um hook em um eventoidempotente
POST/v1/cardscards:writeCria um cartão de visitaidempotente
POST/v1/cards/{id}/deactivatecards:writeDesativa um cartão de visitaidempotente
POST/v1/conversionsconversions:writeRegistra uma conversãoidempotente
POST/v1/nfc/tagscodes:writeRegistra uma tag NFC em um códigoidempotente