ZEVRACentro de Ayuda
IntegracionesAPI de captación

Idempotencia

Reintenta cualquier solicitud sin miedo a crear leads duplicados.

El problema

Tu servidor manda un lead, la red se corta antes de recibir la respuesta y tu código reintenta. Sin protección, ese reintento crea un segundo lead. Con la cabecera Idempotency-Key, el reintento devuelve la respuesta de la primera vez.

Cómo se usa

Manda una cabecera Idempotency-Key con un valor único por envío: entre 1 y 200 caracteres ASCII imprimibles. Buenos candidatos: el id del registro en tu base de datos, el id del envío del formulario, o un UUID que generes antes del primer intento y reutilices en cada reintento.

curl -X POST https://api.zevra.co/v1/leads \
  -H "Authorization: Bearer zvr_live_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: formulario-web-8f3c2a" \
  -d '{ "nombre": "Ana Ruiz", "telefono": "+52 55 0000 0001" }'

La llave se recuerda 24 horas, por origen (por llave de API).

Qué pasa en cada caso

SituaciónRespuesta
Primera vez que vemos la llaveLa solicitud se procesa normal: 201 created o 200 enriched.
Misma llave, mismo cuerpo, dentro de 24 horasLa respuesta original, con el mismo código HTTP y el mismo lead_id, más "replay": true. No se toca el CRM.
Misma llave, cuerpo distinto409 idempotency_conflict. Una llave es de un envío; si el contenido cambió, usa otra.
Misma llave mientras la primera solicitud sigue en curso409 idempotency_in_progress con Retry-After: 1. Espera un segundo y reintenta.
Misma llave después de 24 horasCuenta como la primera vez.

Una respuesta repetida se ve así:

{
  "lead_id": "7b1d9c1e-2f4a-4d0b-9a3c-5e6f7a8b9c0d",
  "status": "created",
  "lead_url": "https://dashboard.zevra.co/dashboard/leads?leadId=7b1d9c1e-2f4a-4d0b-9a3c-5e6f7a8b9c0d",
  "replay": true,
  "request_id": "req_9c2e7a1b4d6f8e0a2c4b6d8f"
}

El request_id es el del reintento (cada intento tiene el suyo), pero lead_id, status y lead_url son los originales.

Solo los éxitos reservan la llave

Esta es la parte que importa cuando algo sale mal. Si una solicitud con Idempotency-Key falla (un 400 por un campo inválido, un 422 porque la propiedad no existe, un 500 nuestro), la llave queda libre. Corrige el cuerpo y vuelve a mandarlo con la misma llave: se procesa como si fuera la primera vez.

Dicho de otra forma: la llave solo se "gasta" cuando te devolvimos un 201 o un 200. Un intento fallido nunca bloquea al reintento corregido.

Comparar cuerpos

Para decidir si dos solicitudes son "la misma", la API compara el cuerpo JSON de forma canónica: el orden de las llaves y los espacios en blanco no importan, los valores sí. {"nombre":"Ana","telefono":"55..."} y { "telefono": "55...", "nombre": "Ana" } son el mismo cuerpo.

Modo de prueba

Una solicitud con X-Zevra-Dry-Run: true nunca reserva ni consulta llaves de idempotencia, aunque la mandes. El modo de prueba no escribe nada, así que no hay nada que proteger.

Recomendaciones

  • Genera la llave antes del primer intento y guárdala junto al registro; así cualquier reintento, incluso desde otro proceso, manda la misma.
  • Un formulario que se envía dos veces por doble clic es el caso típico: usa el id del envío como llave y el segundo clic devuelve la respuesta del primero.
  • Si usas Zapier o Make, la mayoría de los disparadores ya traen un id único del evento. Ese id es tu llave.

En esta página