Conceptos
Idempotencia
Envía una Idempotency-Key con un POST y, si reintentas tras perder la conexión, recibirás la primera respuesta en lugar de inscribir a alguien dos veces.
Por qué
La conexión puede caerse después de que AttendBeacon haya inscrito a alguien, pero antes de que tu código lo sepa. Si envías de nuevo el mismo POST, se emitiría una segunda entrada. Con una Idempotency-Key no ocurre: la segunda solicitud recibe la respuesta de la primera.
Cómo
Genera una clave propia para cada solicitud (un UUID es ideal) y envíala con el POST. Consérvala junto a la solicitud hasta que tengas respuesta, y envía la misma clave en cada reintento.
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))
}
}Qué se guarda y durante cuánto tiempo
La primera respuesta a una clave de idempotencia (su estado, Location y cuerpo) se guarda durante 24 horas y se devuelve de nuevo a cualquier solicitud con la misma clave, método, ruta y cuerpo. Una respuesta repetida lleva Idempotent-Replayed: true.
| Situación | Respuesta |
|---|---|
| Un reintento de una solicitud que ya se respondió | La primera respuesta, con Idempotent-Replayed: true |
| Un reintento mientras la primera todavía se está procesando | 409 idempotencyKeyInUse, con Retry-After: 1 |
| La misma clave con otro método, ruta o cuerpo | 422 idempotencyKeyReused: usa una clave nueva para una solicitud nueva |
| Una clave de más de 255 caracteres, o con espacios | 400 invalidIdempotencyKey |
| La primera solicitud falló en el lado de AttendBeacon (5xx) | No se guarda nada, así que el reintento se procesa desde cero |
Una clave de idempotencia pertenece a la clave de API que la envió: dos integraciones no pueden chocar. Las respuestas guardadas pueden nombrar a una persona; cuando esa persona elimina sus datos, también se eliminan las respuestas que la nombran.
Qué llamadas
Todos los POST aceptan una Idempotency-Key: inscribir, emitir entradas, cancelar, registrar entradas y crear eventos, sesiones, códigos de descuento y webhooks. PATCH, PUT y DELETE se pueden repetir sin riesgo por su propia naturaleza; PATCH acepta en cambio If-Match, para no sobrescribir un cambio hecho mientras tanto (Errores, preconditionFailed).