xYago APIv1

Verify (códigos)

Envía códigos de verificación de un solo uso (inicio de sesión, registro, confirmación de una operación) por el chat de la app de xYago, desde tu remitente verificado. Si el teléfono no usa xYago lo sabes al momento y envías el código por tu otro canal (p. ej. SMS).

Antes de empezar

  1. Necesitas un remitente aprobado (panel → API Developer → Remitentes; ver Mensajes). Verify no usa plantillas: el texto lo pone xYago.
  2. Activa el producto Verify y crea una API key con el scope verify:write (y verify:read para consultar).

Flujo

  1. Tu backend llama a POST /verify/start con el teléfono. xYago genera un código de 6 dígitos, válido 10 minutos y con 5 intentos, y lo envía al chat del usuario (con botón «Copiar código»). La respuesta nunca contiene el código.
  2. El usuario escribe el código en tu app.
  3. Tu backend llama a POST /verify/check con el id de la verificación y el código.
curl -X POST 'https://api.xyago.com/v1/verify/start' \
  -H "Authorization: Bearer $XYAGO_API_KEY" \
  -H "Idempotency-Key: login-7f3a9c2e-0001" \
  -H "Content-Type: application/json" \
  -d '{"to": "+573005550123", "sender_id": "tiendaexpress", "language": "es",
       "reference": "login-CLI-000123"}'

Respuesta 201:

{
  "id": "3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59",
  "object": "verification",
  "sender_id": "tiendaexpress",
  "to_masked": "+57 ••• ••• 0123",
  "channel": "xyago_chat",
  "language": "es",
  "reference": "login-CLI-000123",
  "status": "pending",
  "delivery_status": "sent",
  "failure_code": null,
  "attempts": 0,
  "max_attempts": 5,
  "expires_at": "2026-11-04T15:10:00Z",
  "verified_at": null,
  "created_at": "2026-11-04T15:00:00Z"
}
curl -X POST 'https://api.xyago.com/v1/verify/check' \
  -H "Authorization: Bearer $XYAGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"verification_id": "3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59", "code": "482913"}'
{
  "id": "3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59",
  "valid": true,
  "status": "verified",
  "attempts_remaining": 0
}

Un código erróneo responde 200 con valid: false y los intentos que quedan:

{
  "id": "3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59",
  "valid": false,
  "status": "pending",
  "attempts_remaining": 4
}

Teléfonos sin xYago: tu SMS de respaldo

Si el teléfono no usa xYago, POST /verify/start responde al momento 404 recipient_not_on_xyago: no se envía nada, no queda en espera y no se reintenta. Envía entonces el código por tu canal habitual (SMS, correo...). Si el usuario no puede recibir mensajes de tu remitente la respuesta es 409 undeliverable (trátalo igual: usa tu otro canal).

Estados

EstadoSignificado
pendingCódigo enviado, esperando la comprobación. delivery_status: sent → delivered → read.
verifiedCódigo correcto (ya no sirve).
expiredPasaron los 10 minutos.
too_many_attempts5 códigos erróneos.
canceledSustituida por una verificación más reciente al mismo teléfono.
failedNo se pudo enviar (failure_code: recipient_not_on_xyago, undeliverable...).

Consulta una verificación con GET /verify/{id}.

Idempotency-Key (obligatoria)

POST /verify/start lleva la cabecera Idempotency-Key, como POST /messages. Si la respuesta no llega (timeout, 502, 503), repite con la misma clave y el mismo cuerpo: nunca se envían dos códigos a la vez. Si el primero ya llegó al chat recibes la misma verificación; si no llegó se envía uno nuevo (el anterior deja de valer), pero solo pasada la espera entre códigos desde el intento anterior, dentro de los límites y sin pausa de Verify (si no: 429 rate_limited con Retry-After, o 403 verify_paused; repite después con la misma clave). Misma clave con otro cuerpo: 409 idempotency_key_reused.

Límites

Al superar un límite: 429 rate_limited con Retry-After. Si los límites no se pueden comprobar, POST /verify/start responde 503 limits_unavailable (reintenta con la misma Idempotency-Key); POST /verify/check sigue funcionando (el tope horario de códigos erróneos no se aplica en ese momento, pero sí los 5 intentos, el un solo uso y la caducidad de cada verificación), así que un usuario con su código nunca se queda sin poder entrar. Si muchos de tus códigos van a teléfonos sin xYago, xYago pausa Verify para tu remitente (403 verify_paused; contacta con xYago).

Sandbox

Con una clave xy_test_ solo se envían códigos (reales) a tus números de prueba confirmados (422 sandbox_recipient_not_allowed si no). Mientras tu remitente no está aprobado, el código llega desde xYago Sandbox con el prefijo [Sandbox · Tu remitente].

Webhooks

Suscríbete a verification.delivered, verification.read, verification.verified, verification.expired y verification.failed (también al agotar los intentos). data es la verificación (sin el código):

{
  "created_at": "2026-03-02T15:00:04Z",
  "data": {
    "id": "3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59",
    "object": "verification",
    "sender_id": "tiendaexpress",
    "to_masked": "+57 ••• ••• 0123",
    "channel": "xyago_chat",
    "language": "es",
    "reference": "login-CLI-000123",
    "status": "verified",
    "delivery_status": "read",
    "failure_code": null,
    "attempts": 1,
    "max_attempts": 5,
    "expires_at": "2026-11-04T15:10:00Z",
    "verified_at": "2026-11-04T15:01:12Z",
    "created_at": "2026-11-04T15:00:00Z"
  },
  "environment": "live",
  "id": "evt_4f9c2b7a1d3e4c5b8a6f0e1d2c3b4a59",
  "type": "verification.verified"
}

Buenas prácticas