Conceitos
Idempotência
Envie um Idempotency-Key com um POST, e uma nova tentativa após uma ligação perdida recebe a primeira resposta em vez de inscrever alguém duas vezes.
Porquê
Uma ligação pode cair depois de a AttendBeacon ter inscrito alguém, mas antes de o seu código o saber. Enviado de novo, o mesmo POST emitiria um segundo bilhete. Com um Idempotency-Key isso não acontece: o segundo pedido recebe a resposta do primeiro.
Como
Crie uma chave sua para cada pedido (um UUID é ideal) e envie-a com o POST. Guarde-a com o pedido até ter uma resposta e envie a mesma chave em cada nova tentativa.
curl -X POST "https://attendbeacon.com/api/v1/events/b3ac0000-0000-4000-8000-000000000001/registrations" \
-H "Authorization: Bearer $ATTENDBEACON_KEY" \
-H "Idempotency-Key: 6f1c2a9e-4b7d-4c0f-8e3a-5b9d1c7e0f42" \
-H "Content-Type: application/json" \
-d '{ "ticketTypeId": "b3ac0006-0000-4000-8000-000000000001", "firstName": "Anna", "lastName": "de Vries", "email": "anna.devries@example.com" }'const idempotencyKey = crypto.randomUUID(); // once per registration, kept for its retries
async function register(person) {
for (let attempt = 1; ; attempt++) {
try {
const response = await fetch(`${base}/events/${eventId}/registrations`, {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Idempotency-Key": idempotencyKey, "Content-Type": "application/json" },
body: JSON.stringify(person)
});
if (response.status < 500) return response; // an answer: the first, or the first again
} catch { /* the connection dropped: try again with the same key */ }
if (attempt === 4) throw new Error("AttendBeacon did not answer.");
await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000));
}
}var idempotencyKey = Guid.NewGuid().ToString(); // once per registration, kept for its retries
for (var attempt = 1; ; attempt++)
{
using var request = new HttpRequestMessage(HttpMethod.Post, $"events/{eventId}/registrations") { Content = JsonContent.Create(person) };
request.Headers.Add("Idempotency-Key", idempotencyKey);
try
{
using var response = await client.SendAsync(request);
if ((int)response.StatusCode < 500) return await response.Content.ReadFromJsonAsync<JsonElement>();
}
catch (HttpRequestException) { /* try again with the same key */ }
if (attempt == 4) throw new InvalidOperationException("AttendBeacon did not answer.");
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}idempotency_key = str(uuid.uuid4()) # once per registration, kept for its retries
for attempt in range(1, 5):
try:
response = requests.post(
f"{base}/events/{event_id}/registrations",
headers={"Authorization": f"Bearer {key}", "Idempotency-Key": idempotency_key},
json=person,
timeout=30,
)
if response.status_code < 500:
break # an answer: the first, or the first again
except requests.ConnectionError:
pass # try again with the same key
time.sleep(2 ** attempt)$idempotencyKey = [guid]::NewGuid().ToString() # once per registration, kept for its retries
$headers["Idempotency-Key"] = $idempotencyKey
$body = $person | ConvertTo-Json
foreach ($attempt in 1..4) {
try {
$attendee = Invoke-RestMethod -Method Post -Uri "$base/events/$eventId/registrations" -Headers $headers -ContentType "application/json" -Body $body
break
} catch {
if ($_.Exception.Response -and [int]$_.Exception.Response.StatusCode -lt 500) { throw }
Start-Sleep -Seconds ([math]::Pow(2, $attempt))
}
}O que é guardado, e durante quanto tempo
A primeira resposta a uma chave (o estado, Location e o corpo) é guardada durante 24 horas e devolvida a qualquer pedido com a mesma chave, método, caminho e corpo. Uma resposta repetida traz Idempotent-Replayed: true.
| Situação | Resposta |
|---|---|
| Uma nova tentativa de um pedido que já foi respondido | A primeira resposta, com Idempotent-Replayed: true |
| Uma nova tentativa enquanto o primeiro ainda está a ser tratado | 409 idempotencyKeyInUse, com Retry-After: 1 |
| A mesma chave com outro método, caminho ou corpo | 422 idempotencyKeyReused: use uma chave nova para um pedido novo |
| Uma chave com mais de 255 caracteres, ou com espaços | 400 invalidIdempotencyKey |
| O primeiro pedido falhou do lado da AttendBeacon (5xx) | Nada é guardado, por isso a nova tentativa é tratada de raiz |
Uma chave pertence à chave de API que a enviou: duas integrações não podem colidir. As respostas guardadas podem nomear uma pessoa; quando essa pessoa elimina os seus dados, as respostas que a nomeiam também são eliminadas.
Que chamadas
Todos os POST aceitam um Idempotency-Key: inscrever, emitir bilhetes, cancelar, registar entradas, criar eventos, sessões, códigos de desconto e webhooks. PATCH, PUT e DELETE são, por natureza, seguros de repetir; PATCH aceita antes If-Match, para que uma alteração feita entretanto não seja substituída (Erros, preconditionFailed).