Inyo

Pruebas en Sandbox

El sandbox le permite llevar a un remitente por la verificación de identidad y una transacción por todo su ciclo de vida, sin documentos reales, revisores ni movimiento real de dinero. Cuatro herramientas lo hacen posible:

  • Números de documento reservados que deciden cómo termina la sesión KYC de un remitente.
  • Endpoints de resolución que resuelven una sesión KYC retenida para revisión, o una carga de documento que quedó pendiente — que en sandbox nadie atendería.
  • Valores del remitente sobre los que actúa el propio sandbox de la red de payout.
  • Un endpoint de forzado de eventos que simula los callbacks externos (webhooks del gateway, notificaciones de la red de payout) que normalmente hacen avanzar una transacción.

La página sigue el orden de una integración: primero los remitentes, luego las transacciones.


Remitentes

Validación del código postal

La creación del remitente valida el código postal contra su país. Dos rechazos son fáciles de provocar sin querer al generar datos de prueba:

Código postalPaísResultado
99999, 00000, AAAAAla mayoría422 - los valores de un solo carácter repetido se rechazan como datos de relleno
ABCUS422 - no coincide con el formato postal del país
12345, 12345-6789USaceptado

Si se permiten o no los valores de relleno se configura por país, así que un valor rechazado para un país puede ser aceptado para otro.


Verificación de teléfono

La verificación de identidad por teléfono se simula en sandbox y se decide por el último dígito del número, de modo que puede alcanzar cada rama sin cambiar ninguna configuración. Vea Verificación de teléfono -> Pruebas en sandbox para la tabla completa.


Verificar la identidad de un remitente

Todo el flujo KYC funciona en sandbox, y usted elige cómo termina cada sesión:

  1. Declare un documento en el remitente, con un número reservado.
  2. El número decide el desenlace.
  3. Abra la sesión y lea su resultado en DocumentUpdatedEvents.
  4. Si queda retenida para revisión, resuélvala usted mismo.

En sandbox, toda sesión que alcanza un veredicto queda retenida para revisión — es deliberado, no una mala configuración. No hay umbral de aprobación ni de rechazo configurado, así que un documento limpio y uno defectuoso reportan por igual PENDING_REVIEW, y el veredicto es el que usted resuelve. Solo los desenlaces sin veredicto — FAILED, EXPIRED y SUPERSEDED — son terminales por sí mismos.

Declarar el documento

La creación del remitente verifica el formato de cada documento declarado contra el servicio KYC de Inyo y rechaza un número que la verificación declara inválido, así que un número inventado puede hacer fallar la solicitud antes de que se persista nada. Los números reservados de abajo son números reales y de formato válido, así que pasan: los de licencia y pasaporte vuelven válidos, y los de documento de identidad de EE. UU. vuelven sin evaluar, lo que la creación del remitente también acepta.

Un número reservado se reconoce por su tipo de documento y jurisdicción exactos. Declárelo con countryCode: "US", y una licencia además con stateCode: "PA":

"documents": [
  { "type": "drivers_license", "document": "99900101", "countryCode": "US", "stateCode": "PA" }
]

La sesión verifica el primer documento elegible del remitente — una licencia antes que un pasaporte, y este antes que un documento de identidad — así que el número reservado tiene que estar en ese. Declarar un único documento es la forma más simple de asegurarse.

Para probar la verificación de formato en sí y no la sesión, use en su lugar los números reservados de verificación de formato. Devuelven un veredicto elegido, true, false o null, y no dictan ninguna sesión; false es como llega al rechazo propio de la creación del remitente.

Forzar cada desenlace

El número del documento declarado decide cómo termina la sesión. Los números se listan para drivers_license, id y passport, en ese orden:

NúmeroQué hace la sesiónQué entrega DocumentUpdatedEvents
clean — 99900101, 99900201, 999003001Se completa al crearse con un resultado completoPENDING, y luego VERIFIED o REJECTED cuando usted la resuelve
capture_exhausted — 99900108, 99900208, 999003008Termina al crearse como un remitente que agotó sus intentos de capturaFAILED
link_expired — 99900113, 99900213, 999003013Se crea ya vencida; su enlace muestra el mensaje de enlace vencidoEXPIRED
Cualquier otro númeroEspera una captura real en el widget, analizada como en producciónLo que produzca la captura; EXPIRED si el remitente no termina en 49 horas

SUPERSEDED no necesita número: abra una segunda sesión para el mismo remitente, o vuelva a declarar su documento con otro número, y la sesión anterior termina en SUPERSEDED. Los tres desenlaces sin veredicto se explican en Cuando una sesión termina sin veredicto.

Los demás escenarios con veredicto del catálogo — document_not_authentic, sanctions_hit y el resto — terminan como clean. Un escenario da forma al resultado, no al veredicto: decide qué verificación falla en el resultado que devuelve Consultar una Sesión de Verificación, y el veredicto sigue siendo el que usted resuelve.

La creación de la sesión responde 422 KYC_VALIDATION_ERROR, con el motivo en detail, cuando un escenario no puede ejecutarse en su tenant — se desvía en una verificación que su cuenta no tiene habilitada, como sanctions_hit sin screening de sanciones, o el remitente no tiene nombre, apellido o fecha de nacimiento, que un resultado dictado necesita. No se crea nada.

Solo sandbox. Los números reservados se habilitan por tenant. Si uno deja la sesión esperando una captura, pida a su contacto en Inyo que habilite la simulación en su tenant de sandbox.

Abrir la sesión

Ábrala con la misma solicitud que en producción; nada en ella cambia. Una sesión dictada por un número reservado es definitiva al crearse: la respuesta sigue trayendo un widgetUrl, así que su integración funciona sin cambios, pero no hay nada que capturar detrás.

Lea el resultado en DocumentUpdatedEvents, cuyo id es el id de la sesión. Llega poco después de la respuesta, así que su propio registro de la sesión ya existe. Consultar una Sesión de Verificación es la alternativa cuando se pierde una entrega.

Resolver una sesión KYC retenida

Endpoint: POST /organizations/{tenant}/v2/senders/{personId}/kycSession/{sessionId}/resolve
Autenticación: Nivel agente
Disponibilidad: Solo sandbox/staging — este endpoint no existe en producción.

Nadie revisa sus remitentes de prueba, así que sin este endpoint una sesión retenida nunca alcanza un estado terminal: ningún nivel de cumplimiento cambia y su flujo de DocumentUpdatedEvents se detiene tras el PENDING.

Este endpoint la resuelve. Ejecuta el mismo código que la decisión de un revisor de Inyo, así que el veredicto del documento, el nivel de cumplimiento del remitente y el evento saliente son los reales — solo cambia la puerta.

curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/v2/senders/$PERSON_ID/kycSession/$SESSION_ID/resolve \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --header "x-agent-id: $AGENT_ID" \
  --header "x-agent-api-key: $AGENT_KEY" \
  --data '{
  "outcome": "verified",
  "reason": "Liberada para la prueba de integración"
}'
CampoNotas
outcomeverified o rejected. Obligatorio.
reasonTexto libre, hasta 255 caracteres. Obligatorio — se almacena junto con la resolución.

Responde 200 con la sesión en la misma forma que devuelve Consultar una Sesión de Verificación, con su nuevo status y completedAt. Un suscriptor recibe un segundo DocumentUpdatedEvents con el mismo id que llevaba el PENDING anterior, de modo que ambos se correlacionan.

Cualquier sesión retenida califica, haya sido dictada por un resultado de verificación simulado o capturada de verdad en el widget.

Rechazos

Todo rechazo lleva el status real de la sesión junto a error y message, porque el código por sí solo no le dice qué hacer a continuación — NOT_HELD cubre tanto una sesión aún PENDING (vuelva más tarde) como una ya decidida (deténgase y lea el resultado).

{ "error": "NOT_HELD", "status": "VERIFIED", "message": "This session is VERIFIED, not held for review." }
HTTPerrorSignificado
404NOT_FOUNDNo existe esa sesión para esta persona, en este tenant
409NOT_HELDLa sesión no está retenida para revisión — sigue pendiente, o ya fue decidida
409MANAGED_TENANCYSu tenancy la decide la plataforma de verificación; la retención pertenece a su propia cola
409DECIDED_ELSEWHEREUna sesión posterior ya decidió este documento, así que resolver esta no tendría efecto
409ALREADY_RESOLVEDAlgo resolvió la sesión mientras su petición estaba en curso
422VALIDATION_ERRORFalta outcome o no es uno de los dos valores, o falta reason o es demasiado largo

El endpoint no es idempotente: llamarlo de nuevo tras una resolución exitosa devuelve 409 NOT_HELD con el estado al que quedó, no 200. Ramifique según status en lugar de reintentar a ciegas.


Resolver una revisión de documento pendiente

Endpoint: POST /organizations/{tenant}/v2/senders/{personId}/documents/{documentId}/resolve
Autenticación: Nivel agente
Disponibilidad: Solo sandbox/staging — este endpoint no existe en producción.

Las cargas quedan en PENDING en sandbox: el OCR con IA normalmente está desactivado y nadie atiende la cola de revisión de un tenant de prueba. El remitente nunca sube de nivel y el flujo de DocumentUpdatedEvents se detiene tras el primer PENDING.

Esto lo resuelve por la vía real: la misma fila de estado de verificación, el mismo webhook y el mismo resultado de cumplimiento que produce la decisión de un revisor. verifiedBy lleva el prefijo sandbox:, de modo que una decisión simulada sigue siendo distinguible.

Cualquier documento de la persona califica, no solo la prueba de origen de fondos. Un documento de identidad subido por los endpoints de carga directa se queda igual de atascado y lo leen las mismas comprobaciones.

curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/v2/senders/$PERSON_ID/documents/$DOCUMENT_ID/resolve \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --header "x-agent-id: $AGENT_ID" \
  --header "x-agent-api-key: $AGENT_KEY" \
  --data '{
  "outcome": "verified",
  "reason": "Aprobado para el recorrido de integración"
}'

$DOCUMENT_ID es el id que devolvió la respuesta de la carga.

CampoNotas
outcomeverified o rejected. Obligatorio.
reasonTexto libre, hasta 255 caracteres. Obligatorio — se almacena con la resolución y se muestra al revisor.

Responde 200 con el nuevo estado del documento en la misma forma que devuelve Consultar el estado de verificación actual:

{
  "status": "verified",
  "reason": "Aprobado para el recorrido de integración",
  "verifiedBy": "sandbox:8f14e45f-ceea-467a-9f5e-9b2c4c9a1f77",
  "createdAt": "2026-09-29T12:10:37+00:00"
}

Un suscriptor recibe un DocumentUpdatedEvents con el id del documento y el nuevo verificationStatus.

Rechazos

Todo rechazo lleva el status real del documento junto a error y message, porque el código por sí solo no le dice qué hacer a continuación.

{ "error": "ALREADY_DECIDED", "status": "verified", "message": "This document is already verified; it is not waiting for a review." }
HTTPerrorSignificado
404NOT_FOUNDNo existe ese documento para esta persona, bajo este tenant
409ALREADY_DECIDEDEl documento ya está verified o rejected — lea el resultado en lugar de resolverlo otra vez
409NOT_PENDINGEl documento está en otro estado que este endpoint no resuelve
422VALIDATION_ERRORFalta outcome o no es uno de los dos valores, o falta reason o es demasiado largo

Este endpoint resuelve un documento que está esperando; no reabre uno ya decidido. No es idempotente — llamarlo de nuevo tras una resolución exitosa devuelve 409 ALREADY_DECIDED con el estado al que quedó, no 200. Ramifique según status en lugar de reintentar a ciegas.


Transacciones

Forzar transiciones de estado

Endpoint: POST /organizations/{tenant}/fx/transactions/{transactionId}/events
Autenticación: Nivel de agente
Disponibilidad: Solo sandbox/staging — este endpoint no existe en producción.

curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/events \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --header "x-agent-id: $AGENT_ID" \
  --header "x-agent-api-key: $AGENT_KEY" \
  --data '{
  "event": "payment_authorized",
  "message": "Optional description"
}'

Eventos disponibles

EventoVálido desde el estadoQué simula
payment_authorizedWaitingChallenge3dsEl callback 3DS del gateway. Reanuda el flujo — despacha la integración del payout. Úselo cuando la transacción esté atascada esperando el 3DS.
ach_settledWaitingSettlementEl callback de liquidación ACH. Mueve la transacción a través de PaymentSettled → PaymentCaptured y activa la confirmación del payout.
payment_capturedReviewApprovedUna confirmación de captura de tarjeta. Activa la confirmación del payout.
payout_paidPayoutAccepted, PayoutReleased, WaitingPayoutLa red de payout confirmando que el beneficiario recibió los fondos. Mueve la transacción a Paid → Completed.
payout_voidPayoutAccepted, PayoutHold, PayoutReleased, WaitingPayoutLa red de payout anulando la transacción. Según la configuración del tenant, se revierte automáticamente o queda en PendingReversalApproval.
hold_releasedPayoutHoldLa red de payout liberando una retención de cumplimiento. Reanuda el flujo.
cancelledCualquier estado cancelableCancelación iniciada por el cliente (anula el pago y cancela el payout).
refundedEstados reembolsablesMarca la transacción como reembolsada.

Si la transacción no está en un estado válido para el evento, el endpoint devuelve un error explicando el estado actual.

Flujo de prueba típico (Tarjeta + 3DS)

  1. Cree la transacción → estado WaitingChallenge3ds, paymentStatus: ActionRequired
  2. Fuerce el 3DS: { "event": "payment_authorized" }
  3. La transacción progresa automáticamente → PayoutAccepted → ManualReview → ReviewApproved → PaymentCaptured → WaitingPayout
  4. Fuerce la entrega: { "event": "payout_paid" }
  5. La transacción llega a Completed

Flujo de prueba típico (ACH)

  1. Cree la transacción → progresa a WaitingSettlement
  2. Fuerce la liquidación: { "event": "ach_settled" }
  3. La transacción progresa automáticamente → PaymentSettled → PaymentCaptured → WaitingPayout
  4. Fuerce la entrega: { "event": "payout_paid" }
  5. La transacción llega a Completed

Escenarios de prueba de la red de payout

El sandbox envía los datos de su remitente a la red de payout exactamente como lo hace producción, y el sandbox de la propia red decide qué sucede después. Dos valores del remitente producen un resultado deliberado. Todo lo demás se acepta y se paga.

Retención de cumplimiento

El nombre de remitente JULIO SOLANO provoca una retención de cumplimiento en la red de payout:

{ "firstName": "JULIO", "lastName": "SOLANO", "...": "..." }

La transacción llega a PayoutHold tras la integración del payout, con complianceStatus: Pending y payoutStatus: Processing. Libérela con el evento forzado hold_released (o espere a que la red o el backoffice la liberen).

Flujo esperado:

... -> ProcessingPayout -> PayoutHold -> [retención liberada] -> PayoutReleased -> ...

Remitente rechazado por la red

Un remitente cuyo estado y código postal no concuerdan es rechazado por la red de payout antes de crear la transferencia. 96738 es un código postal de Hawái, así que enviarlo con una dirección de California produce esto:

{ "address": { "stateCode": "CA", "city": "Los Angeles", "zipcode": "96738" } }

La transacción no se convierte en un rechazo que usted pueda tratar como tal. Termina en Failed / Failed y queda estacionada para intervención manual, porque un problema de datos se trata como recuperable y no como un rechazo de la transferencia:

complianceStatusFailed
payoutStatusFailed
MensajePayout integration error. Please contact support if this persists.

El mensaje es genérico a propósito: el texto de error de la propia red de payout no se expone en la API. Si encuentra esto fuera de una prueba, la causa casi siempre es una dirección del remitente que la red no puede resolver.

Rechazo del pago

Use un token de tarjeta que el sandbox de la pasarela rechace, o un token vencido. La transacción llega a PaymentDeclined -> Cancelled.

Rechazo de 3DS

Cuando sea redirigido a la página del desafío 3DS, elija "Rechazar" (donde el sandbox lo ofrezca). La pasarela reporta la falla y la transacción pasa a PaymentDeclined -> Cancelled.


Tarjetas de prueba

Use tarjetas de prueba del sandbox para simular escenarios de 3DS. La lista completa está en tarjetas de prueba del Payments Gateway. Las más comunes:

Número de tarjetaMarcaComportamiento 3DS
4462030000000000VISADesafío 3DS
4035874000424977VISASin fricción (sin desafío)
5425230000004415MastercardDesafío 3DS
4000000000000002VISARechazada

Tokenícelas mediante el SDK del gateway, luego pase el token como paymentMethod.token al crear una cuenta de fondeo.


Consulta de estado por polling

Después de crear una transacción, consulte GET /organizations/{tenant}/fx/transactions/{transactionId} y observe:

CampoQué esperar
complianceStatusPending → Approved (después de la integración del payout)
payoutStatusPending → Processing → Completed
paymentStatusActionRequired → null (después de que el 3DS se completa)
receiptDivulgaciones regulatorias (disponibles desde la creación)

Para el rastro de auditoría completo de transiciones, use GET /fx/transactions/{transactionId}/status — vea Transacción. En producción, prefiera los webhooks en lugar del polling.