Webhooks del API Bancario

API 1.0.0

El grupo Webhooks permite registrar el destino HTTPS donde recibís los eventos, ver qué features podés suscribir por método de pago y consultar el contrato de payload de cada evento antes de escribir el receptor.

POST /api/public/v1/webhook/destination
GET  /api/public/v1/webhook/features
GET  /api/public/v1/webhook/event-contracts/{event_type}

Eventos#

Nuestro API te notifica cuatro tipos de evento:

EventoCuándo llega
payin.postedSe acreditó un movimiento de entrada.
payment.succeededEl pago terminó de forma exitosa.
payment.failedEl pago terminó con falla.
payment.reversedEl pago fue revertido.

Un pago revertido conserva el estado posted y se distingue por result: reversed y has_reversal: true; ver estados del pago.

Buenas prácticas del receptor#

La entrega se intenta hasta 3 veces por evento y se considera exitosa con cualquier HTTP 2xx. Respondé 2xx en cuanto recibís el evento y procesá aparte: un receptor lento provoca reenvíos. Tratá los eventos como potencialmente repetidos y deduplicá por event.event_id del sobre (los reintentos reenvían el mismo evento con el mismo event_id). Y no confíes el estado final sólo al webhook: confirmalo con la consulta del pago.

Operaciones#

Crear destino de webhook

POST /api/public/v1/webhook/destination

Host: https://api-baas-sandbox.tilopay.com — requiere Authorization: Bearer <access_token>.

Los campos válidos dependen de destination_kind y scope_kind. Enviá solo los campos que aplican a tu combinación; los demás campos de alcance deben omitirse (no como marcadores null).

Payloads de eventos Inspeccioná los contratos salientes con GET /api/public/v1/webhook/event-contracts/{event_type} (payin.posted, payment.succeeded, payment.failed, payment.reversed).

destination_kind

  • EXTERNAL_WEBHOOK (único tipo disponible en el API pública): requiere webhook_url (debe empezar con https://) y secret_ref. Ajustes opcionales de entrega (timeout_ms, max_attempts). Los payloads siempre van cifrados con AES_GCM usando secret_ref; el cuerpo HTTP es {"data":"<ciphertext>"}.
  • INTERNAL está reservado para la administración de la plataforma y no puede crearse por este endpoint.

scope_kind

  • TENANT: omití scope_account_id, scope_owner_type y scope_owner_id.
  • ACCOUNT: enviá scope_account_id; omití los campos de owner.
  • OWNER: enviá scope_owner_type y scope_owner_id; omití scope_account_id. scope_owner_type es uno de tenant, partner, user, platform, customer (en minúsculas en el payload).

Cifrado del webhook (EXTERNAL_WEBHOOK)

Las entregas siempre envían Content-Type: application/json y Accept: application/json.

El cuerpo HTTP siempre es {"data": "<base64>"}. Los bytes decodificados de base64 son:

nonce (12 bytes, random) || AES-256-GCM ciphertext || 16-byte auth tag
key                   = SHA256(secret_ref)              # 32 bytes → AES-256
wire                  = base64_decode(data)
nonce, sealed         = wire[:12], wire[12:]             # sealed = ciphertext || tag
plaintext             = AES-GCM-Open(key, nonce, sealed, additional_data = none)

No se usan datos autenticados adicionales (AAD). plaintext es el mismo sobre JSON documentado por tipo de evento en GET /webhook/event-contracts/{event_type}: descifrá primero y luego aplicá ese contrato. La autenticidad la da la etiqueta de autenticación GCM (el descifrado falla si el cuerpo fue alterado).

Deduplicá los reintentos con event.event_id del sobre del payload: los reintentos de entrega pueden reenviar el mismo evento con el mismo event_id.

La entrega se intenta hasta 3 veces por evento. Cualquier respuesta HTTP 2xx se considera exitosa.

Parámetros

ParámetroEnTipoObligatorioDescripción
X-Correlation-IdheaderstringnoId de correlación opcional que enviás para trazar la solicitud de punta a punta. Se devuelve como correlation_id en el sobre de respuesta. Si lo omitís, la API genera uno y igual lo devuelve.

Cuerpo del request

CampoTipoObligatorioDescripción
feature_idstring (uuid)síFunción a suscribir (de GET /api/public/v1/webhook/features). Debe existir o la creación falla.
is_enabledbooleannoPor defecto true si se omite.
destination_kindstringsíLa API pública solo acepta EXTERNAL_WEBHOOK (callback HTTPS con secreto compartido). Los destinos INTERNAL se administran internamente.Valores: EXTERNAL_WEBHOOK
destination_refstringnoNo se usa en la API pública. Reservado para destinos internos.Puede ser null.
webhook_urlstring (uri)noObligatorio cuando destination_kind = EXTERNAL_WEBHOOK (debe usar https://).Puede ser null.
secret_refstringnoObligatorio cuando destination_kind = EXTERNAL_WEBHOOK: secreto compartido para descifrar los payloads AES_GCM (key = SHA256(secret_ref)). Ver "Cifrado del webhook" en la descripción de esta operación.Puede ser null.
signature_algostringnoFijo en AES_GCM para EXTERNAL_WEBHOOK. Se omite al crear; se guarda como AES_GCM. HMAC_SHA256 ya no se admite.Puede ser null.Valores: AES_GCM
replay_window_secintegernoReservado para destinos históricos. No se usa en entregas AES_GCM de EXTERNAL_WEBHOOK.Puede ser null.
timeout_msintegernoTimeout HTTP de los intentos de entrega (EXTERNAL_WEBHOOK).Puede ser null.
max_attemptsintegernoIntentos de entrega por evento, hasta 3.Puede ser null.
scope_kindstringsí- TENANT: sin campos de id de alcance. - ACCOUNT: enviá solo scope_account_id. - OWNER: enviá solo scope_owner_type y scope_owner_id.Valores: TENANT, ACCOUNT, OWNER
scope_account_idstring (uuid)noObligatorio cuando scope_kind es ACCOUNT. Debe omitirse para TENANT y OWNER.Puede ser null.
scope_owner_typestringnoObligatorio cuando scope_kind es OWNER. Valores permitidos (en minúsculas): tenant, partner, user, platform, customer. Se omite para TENANT y ACCOUNT.Puede ser null.
scope_owner_idstring (uuid)noObligatorio cuando scope_kind es OWNER. Se omite para TENANT y ACCOUNT.Puede ser null.

Ejemplo de request

external_webhook_tenant

{
  "feature_id": "11111111-1111-1111-1111-111111111111",
  "destination_kind": "EXTERNAL_WEBHOOK",
  "scope_kind": "TENANT",
  "webhook_url": "https://example.com/hooks/tilopay",
  "secret_ref": "whsec_example_shared_secret"
}

Respuesta 201

El recurso se creó correctamente.

CampoTipoObligatorioDescripción
idstring (uuid)no—
feature_idstring (uuid)no—
is_enabledbooleanno—
destination_kindstringno—
destination_refstringno—
webhook_urlstringno—
secret_refstringno—
signature_algostringno—
replay_window_secintegerno—
timeout_msintegerno—
max_attemptsintegernoIntentos de entrega por evento, hasta 3.
scope_kindstringno—
scope_account_idstring (uuid)no—
scope_owner_typestringno—
scope_owner_idstring (uuid)no—
created_atstringno—
updated_atstringno—

Ejemplo de respuesta

{
  "success": true,
  "http_status_code": 201,
  "response_code": "CREATED",
  "message": "The resource was created successfully.",
  "correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "data": {
    "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "feature_id": "11111111-1111-1111-1111-111111111111",
    "is_enabled": true,
    "destination_kind": "EXTERNAL_WEBHOOK",
    "destination_ref": null,
    "webhook_url": "https://example.com/hooks/tilopay",
    "secret_ref": "whsec_example_shared_secret",
    "signature_algo": "AES_GCM",
    "replay_window_sec": null,
    "timeout_ms": 10000,
    "max_attempts": 8,
    "scope_kind": "TENANT",
    "scope_account_id": null,
    "scope_owner_type": null,
    "scope_owner_id": null,
    "created_at": "2026-01-15T12:00:00Z",
    "updated_at": "2026-01-15T12:00:00Z"
  }
}

Respuestas de error

HTTPresponse_codeDescripción
400INVALID_REQUESTInvalid request. Check the required fields and try again.
401UNAUTHORIZEDUnauthorized. Verify your session or credentials.
403FORBIDDENYou do not have permission to perform this action.
500INTERNAL_ERRORAn unexpected error occurred. Please try again later.
503SERVICE_UNAVAILABLEA required service is temporarily unavailable. Please try again later.

Listar funciones de webhook por método de pago

GET /api/public/v1/webhook/features

Host: https://api-baas-sandbox.tilopay.com — requiere Authorization: Bearer <access_token>.

Devuelve los IDs de función y los event_types que cada una puede emitir. El alcance del comercio se toma del token de acceso; no enviés tenant_id.

Nombres: los event_types usan payment.* para resultados a nivel de recurso (payment.succeeded, payment.failed, payment.reversed) y payin.posted solo para la dirección entrante (fondos recibidos). No existe payout.posted ni eventos transaction.*. El objeto JSON entregado siempre es payment.

Inspeccioná las formas de payload con GET /api/public/v1/webhook/event-contracts/{event_type} (payin.posted, payment.succeeded, payment.failed, payment.reversed).

Parámetros

ParámetroEnTipoObligatorioDescripción
X-Correlation-IdheaderstringnoId de correlación opcional que enviás para trazar la solicitud de punta a punta. Se devuelve como correlation_id en el sobre de respuesta. Si lo omitís, la API genera uno y igual lo devuelve.
country_codequerystringno—
payment_method_codequerystringno—
webhook_enabledquerybooleanno—

Respuesta 200

La solicitud se procesó correctamente.

CampoTipoObligatorioDescripción
itemsarray<object>no—
items[].idstring (uuid)sí—
items[].event_typesarray<string>síTipos de evento públicos que esta función puede emitir. payment.* = resultados a nivel de recurso (cualquier dirección). payin.posted = solo dirección entrante (fondos recibidos). No existe payout.posted ni transaction.*. Inspeccioná las formas de payload con GET /api/public/v1/webhook/event-contracts/{event_type}.

Ejemplo de respuesta

{
  "success": true,
  "http_status_code": 200,
  "response_code": "OK",
  "message": "The request was processed successfully.",
  "correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "data": {
    "items": [
      {
        "id": "11111111-1111-1111-1111-111111111111",
        "event_types": [
          "payin.posted",
          "payment.succeeded",
          "payment.failed",
          "payment.reversed"
        ]
      }
    ]
  }
}

Respuestas de error

HTTPresponse_codeDescripción
401UNAUTHORIZEDUnauthorized. Verify your session or credentials.
403FORBIDDENYou do not have permission to perform this action.
500INTERNAL_ERRORAn unexpected error occurred. Please try again later.
503SERVICE_UNAVAILABLEA required service is temporarily unavailable. Please try again later.

Contrato de evento de webhook

GET /api/public/v1/webhook/event-contracts/{event_type}

Host: https://api-baas-sandbox.tilopay.com — requiere Authorization: Bearer <access_token>.

Devuelve el contrato del payload saliente de webhook para un tipo de evento público (schema_version 1.0.0). El objeto del sobre siempre es payment (nunca payin, payout o transaction).

Cuándo usar cada nombre

  • payment — recurso y sobre. Eventos de resultado: payment.succeeded, payment.failed, payment.reversed (tanto PAYIN como PAYOUT). Se correlaciona con /transactions/payments de REST.
  • payin / payout — dirección (PAYIN entrante, PAYOUT saliente). payin.posted es solo entrante (registrado en el ledger / fondos recibidos). No existe un payout.posted público.
  • transaction — solo prefijo de ruta REST. No se usa en event_type ni en los objetos del payload.

Valores de event_type admitidos:

  • payin.posted — un pago entrante (PAYIN) llegó al estado público posted. El campo del sobre es payment. payment.local_payment es un booleano JSON (true/false), no una cadena.
  • payment.succeeded — el proveedor confirmó el éxito de un pago (PAYIN o PAYOUT; PIN o SINPE Móvil). Resultado a nivel de recurso, no específico de dirección.
  • payment.failed — el procesamiento terminó en un fallo definitivo (status=failed público), con payment.error estructurado (domain / platform). Cuando el proveedor rechazó el pago, el sobre también incluye rejection. No se usa para reversos posteriores.
  • payment.reversed — reverso posterior de un pago que ya había avanzado (típicamente status=posted o confirmed). REST result=reversed y has_reversal=true. El status de procesamiento no cambia. No es un payment.failed.

Los valores de event_type desconocidos (incluido el interno payment.status_changed) devuelven 404.

Códigos de rechazo SINPE (rejection, solo en payment.failed)

Cuando event_type = payment.failed y el proveedor rechazó el pago, el sobre incluye rejection (mismo objeto y catálogo que data.rejection en POST /accounts/validate). rejection.code es uno de un conjunto fijo de semánticas normalizadas (ver la tabla de valores más abajo). La mayoría de filas de la tabla NO tienen un `code` propio: solo un subconjunto de valores de reason_code tiene una semántica dedicada; cualquier otro reason_code, incluso uno listado abajo con un message específico, devuelve code = SINPE_REJECTED (ej. reason_code = 21 "fondos insuficientes" igual devuelve SINPE_REJECTED). Un reason_code que no aparece aquí igual produce respuesta: message cae a un texto genérico y code a SINPE_REJECTED.

Cuenta / perfil / límites

CodeMessage
21Cuenta Cliente con fondos insuficientes
22Cuenta Cliente no admite créditos
23Cuenta Cliente cerrada
24Cuenta Cliente inactiva
25Cuenta Cliente no admite débitos
26Cuenta Cliente no es de fondos
27Moneda de la Cuenta Cliente no corresponde
28Cuenta cliente no existe
29Cuenta Cliente no registrada en el SINPE
30Cuenta Cliente no habilitada para el servicio
31Cuenta Cliente bloqueada
32Id cliente destino no coincide con registrado en la entidad
33Nombre del cliente destino no coincide con el registrado en la entidad
34Cuenta Cliente en proceso de cierre
35Cuenta Cliente embargada
36Cuenta Cliente con retención judicial
37Cuenta de expediente simplificado no permite el monto indicado
38Límite transaccional de la Cuenta Cliente excedido
39Cuenta Cliente incorrecta
40IBAN de la cuenta destino inválido
41IBAN de la cuenta origen inválido
42Tipo de cuenta no permite la transacción
43Cuenta Cliente no pertenece a la entidad indicada
44Producto de la cuenta no admite el servicio
45Cuenta Cliente en estado de cancelación
46Cuenta Cliente restringida por política de la entidad
47Cuenta Cliente no permite pagos inmediatos
48Cuenta Cliente no permite SINPE Móvil
49Titular de la cuenta destino fallecido
50Cuenta Cliente consolidada o migrada
51Identificación del cliente origen no coincide
52Identificación del cliente destino inválida
53Cliente destino no autorizado para recibir el pago
54Cliente origen no autorizado para enviar el pago
55Perfil del cliente origen no permite la transacción
56Monto inferior al mínimo permitido
57Monto superior al máximo permitido
58Cantidad de transacciones diarias excedida
59Cantidad de transacciones mensuales excedida
60Límite acumulado diario excedido
61Límite acumulado mensual excedido
62Comisión no pudo ser aplicada
63Tipo de cambio no disponible
64Transacción rechazada por control de lavado de dinero
65Transacción rechazada por listas de control
66Transacción en revisión de cumplimiento
67Documento de respaldo requerido no presente
68Firma o autenticación inválida
69Token o segundo factor inválido
70Sesión de usuario expirada
71Usuario no autorizado para el canal
72Dispositivo no registrado
73Geolocalización no permitida
74Operación no soportada en la moneda indicada
75Operación no soportada para el tipo de cliente
76Problemas de comunicación
77Tiempo de espera agotado en la entidad origen
78Tiempo de espera agotado en el SINPE
79Error interno de la entidad origen
80Error interno de la entidad destino
81Entidad origen no disponible
82Entidad destino no encontrada
83Problemas en la respuesta del destino
84Respuesta de la entidad origen incorrecta
85Mensaje con formato electrónico inválido
86Versión del estándar electrónico no soportada
87Campo obligatorio no informado
88Campo con valor fuera de catálogo
89Checksum o integridad del mensaje inválida
90Referencia SINPE duplicada
91Moneda no corresponde
92Transacción no autorizada por entidad destino
93Transacción no autorizada por cliente destino
94Transacción no autorizada por entidad origen
95Transacción no autorizada por cliente origen
96Reverso no permitido para el estado de la transacción
97Reverso ya aplicado
98Confirmación no permitida para el estado de la transacción
99Liquidación no permitida para el estado de la transacción
100Entidad Destino no disponible para procesar en tiempo real
101Entidad origen no disponible para procesar en tiempo real
102Servicio PIN no habilitado para la entidad destino
103Servicio PIN no habilitado para la entidad origen
104Código de entidad destino inválido
105Código de entidad origen inválido
106Código de país de la entidad destino inválido
107Código de país de la entidad origen inválido
108Número de referencia interna inválido
109Número de referencia SINPE inválido
110Transacción no se encuentra en un estado que permita la consulta

Compensación con entidad destino

CodeMessage
201Tiempo respuesta excedido por la entidad destino
202Respuesta de la entidad destino incorrecta según el estándar electrónico
203Se recibió una excepción de la entidad destino
204Error de comunicación con la entidad destino
205Falló procesamiento en el SINPE
206Transacción no autorizada por cliente destino
207Perfil transaccional del cliente destino no permite recibir el pago
208Operación no autorizada entre las entidades participantes
209Pendientes de Cumplimiento
210Conciliación de la transacción no fue posible

Identificación

CodeMessage
801Identificación inválida
802Identificación del cliente origen no encontrada
803Identificación del cliente destino no encontrada
804Identificación no vigente
805Identificación vencida
806Identificación no corresponde al tipo indicado
807Tipo de identificación inválido
808País de la identificación no soportado
809Identificación de menor de edad no permitida
810Identificación de persona jurídica no permitida para el servicio

Validación de formato / Core Bancario

CodeMessage
1002El Id de cliente destino no cumple con el formato esperado por el SINPE
1003El Id de cliente origen no fue informado
1004El Id de cliente origen no cumple con el formato esperado por el SINPE
1005Monto con formato inválido
1006Moneda con formato inválido
1007Problemas de comunicación con el Core Bancario
1008Core Bancario no disponible
1009El valor para el campo no puede ser nulo o infringir su longitud mínima o máxima
1010El valor para el campo no corresponde al tipo de dato esperado
1011El valor para el campo no corresponde al catálogo permitido
1012Fecha con formato inválido
1013Hora con formato inválido
1014Número de referencia con formato inválido
1015IBAN con formato inválido

Canal / tipo de identificación

CodeMessage
1040Canal no informado
1041Canal inválido
1042Canal no corresponde
1043Canal no habilitado para la entidad
1044Canal no habilitado para el servicio
1045El formato de la identificación es inválido
1046Tipo de identificación no informado
1080Tipo de identificación no corresponde al cliente origen
1081Tipo de identificación no corresponde al cliente destino
1082Tipo de identificación no vigente
1083Tipo de identificación no soportado por el servicio
1084Tipo de identificación inválido
1085Tipo de identificación no soportado por la entidad

SINPE Móvil (monedero)

CodeMessage
15300El número de teléfono origen indicado es inválido
15301El número de teléfono origen no tiene activo el Servicio Monedero
15302El número de teléfono destino indicado es inválido
15303El número de teléfono destino no está registrado en el padrón móvil del BCCR
15304No es posible inactivar el monedero indicado pues no existe
15305El número de teléfono indicado ya se encuentra activo como monedero en el padrón local

Delivery body is JSON. EXTERNAL_WEBHOOK HTTP body is always {"data":"<ciphertext>"} (AES-256-GCM, key = SHA256(secret_ref)). Decrypt to obtain the event envelope documented per GET /webhook/event-contracts/{event_type}; the decrypted plaintext matches that contract.

Valores de `rejection.code`

ValorSignificado
ACCOUNT_CLOSEDCuenta cerrada
ACCOUNT_NOT_FOUNDCuenta no encontrada
ACCOUNT_BLOCKEDCuenta bloqueada
INVALID_ACCOUNTCuenta inválida
IDENT_MISMATCHLa identificación no coincide
IDENT_TYPE_INVALIDTipo de identificación inválido
INVALID_ID_FORMATFormato de identificación inválido
VALIDATION_ERRORError de validación
CURRENCY_MISMATCHLa moneda no corresponde
NOT_AUTHORIZED_BY_RECIPIENTNo autorizado por el destinatario
PROFILE_NOT_ALLOWEDPerfil no permitido
CHANNEL_NOT_RECOGNIZEDCanal no reconocido
COMMUNICATION_ERRORError de comunicación
RETRY_LATERReintentar más tarde
WALLET_PHONE_ORIGIN_INVALIDTeléfono origen inválido (SINPE Móvil)
WALLET_PHONE_ORIGIN_NOT_ENABLEDTeléfono origen sin servicio de monedero
WALLET_PHONE_DEST_INVALIDTeléfono destino inválido (SINPE Móvil)
WALLET_PHONE_NOT_REGISTERED_BCCRTeléfono no registrado en el padrón móvil del BCCR
WALLET_NOT_FOUNDMonedero no encontrado
WALLET_PHONE_ALREADY_ACTIVEEl teléfono ya tiene un monedero activo
PHONE_NOT_REGISTEREDTeléfono no registrado
SINPE_REJECTEDRechazado por SINPE (valor genérico de respaldo)

La entrega se intenta hasta 3 veces por evento. Cualquier respuesta HTTP 2xx se considera exitosa.

Parámetros

ParámetroEnTipoObligatorioDescripción
X-Correlation-IdheaderstringnoId de correlación opcional que enviás para trazar la solicitud de punta a punta. Se devuelve como correlation_id en el sobre de respuesta. Si lo omitís, la API genera uno y igual lo devuelve.
event_typepathstringsí—Valores: payin.posted, payment.succeeded, payment.failed, payment.reversed

Respuesta 200

La solicitud se procesó correctamente.

CampoTipoObligatorioDescripción
event_typestringsí—Valores: payin.posted, payment.succeeded, payment.failed, payment.reversed
schema_versionstringsí—
descriptionstringsí—
exampleobjectsí—

Ejemplo de respuesta

{
  "success": true,
  "http_status_code": 200,
  "response_code": "OK",
  "message": "The request was processed successfully.",
  "correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "data": {
    "event_type": "payment.succeeded",
    "schema_version": "1.0.0",
    "description": "Envelope delivered for `payment.succeeded` (decrypt AES_GCM `data` field first).",
    "example": {
      "event_id": "evt_01JEXAMPLE",
      "event_type": "payment.succeeded",
      "schema_version": "1.0.0"
    },
    "x-description-es": "Sobre entregado para `payment.succeeded` (descifrá primero el campo `data` AES_GCM)."
  }
}

Respuestas de error

HTTPresponse_codeDescripción
401UNAUTHORIZEDUnauthorized. Verify your session or credentials.
403FORBIDDENYou do not have permission to perform this action.
404NOT_FOUNDThe requested resource was not found.
500INTERNAL_ERRORAn unexpected error occurred. Please try again later.
503SERVICE_UNAVAILABLEA required service is temporarily unavailable. Please try again later.

Contratos de payload#

Cada evento tiene su propia estructura de datos. Esto es lo que te enviamos en cada uno:

payin.posted

CampoTipoObligatorioDescripción
schema_versionstringsí—
eventobjectsí—
event.event_idstring (uuid)sí—
event.event_typestringsí—Valores: payin.posted
event.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
event.correlation_idstringsí—
contextobjectsí—
context.country_codestringsí—
context.payment_method_codestringsíCatálogo público de métodos de pago. Mismos valores que en REST (PIN, SINPE_MOVIL). Nunca se exponen códigos internos.Valores: PIN, SINPE_MOVIL
paymentobjectsí—
payment.payment_idstring (uuid)sí—
payment.public_idinteger (int64)síEs el mismo identificador: número en webhooks y texto en REST. Tratalo siempre como texto.
payment.statusstringsíCatálogo público de estados del pago. Siempre en minúsculas. Mismos valores en REST (crear, listar, obtener) y en webhooks (payment.status). Los estados internos del ciclo de vida se consolidan: - pending: iniciado, validado, aceptado - processing: procesando, pendiente de procesamiento, registrando - confirmed: confirmado por el riel, aún no registrado en el ledger - posted: registrado en el ledger (se mantiene posted tras un reverso posterior) - failed: fallido, rechazado, posting_failed, cancelado, expirado Un reverso no es un estado. Usá result=reversed / has_reversal=true en REST y el webhook payment.reversed. El detalle operativo queda en status_detail (nombre interno en mayúsculas) en crear/listar/obtener de REST. El webhook payment.failed usa error.platform.code para distinguir fallos de registro de rechazos del proveedor.Valores: pending, processing, confirmed, posted, failed
payment.amountobjectsí—
payment.amount.amountstringsíMonto como texto decimal con exactamente 2 decimales (ej. "1000.00").
payment.amount.currencystringsíMoneda ISO 4217 del pago.Valores: CRC, USD
payment.posted_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
payment.accountobjectsí—
payment.account.typestringsí—
payment.account.valuestringsí—
payment.referenceobjectsí—
payment.reference.client_referencestringsíReferencia del comercio enviada al crear el pago (client_reference), no detail_reference.
payment.reference.external_referencestringsí—
payment.destination_phone_numberstringno—
payment.local_paymentbooleannoBooleano JSON (true/false), no las cadenas "true"/"false".
payment.origin_client_namestringno—
providerobjectsíFoto compartida del proveedor en las lecturas REST de pagos y en los sobres de webhook. La referencia del riel solo está en payment.external_reference, no se duplica aquí. Los provider_status_* coinciden con rejection.reason_code / message / code del webhook cuando el proveedor rechazó la operación. null cuando no hay rechazo.
provider.correlation_idstringsíProvider transaction correlation identifier. Sent to SINPE as the correlation id. Not channel_reference (e.g. MOBILE_APP). Distinct from webhook event.correlation_id (HTTP request id for this delivery). null when unknown.Puede ser null.
provider.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
provider.provider_status_codestringsíMotivo/código crudo del proveedor (ej. 31). Mismo significado que rejection.reason_code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_descstringsíTexto Motivo del proveedor (prefiere Detalle). Mismo significado que rejection.message del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_semanticstringsíSemántica normalizada de la plataforma (ej. ACCOUNT_BLOCKED). Mismo significado que rejection.code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.

payment.succeeded

CampoTipoObligatorioDescripción
schema_versionstringsí—
eventobjectsí—
event.event_idstring (uuid)sí—
event.event_typestringsí—Valores: payment.succeeded
event.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
event.correlation_idstringsí—
contextobjectsí—
context.country_codestringsí—
context.payment_method_codestringsíCatálogo público de métodos de pago. Mismos valores que en REST (PIN, SINPE_MOVIL). Nunca se exponen códigos internos.Valores: PIN, SINPE_MOVIL
paymentobjectsí—
payment.payment_idstring (uuid)sí—
payment.public_idinteger (int64)síEs el mismo identificador: número en webhooks y texto en REST. Tratalo siempre como texto.
payment.statusstringsíCatálogo público de estados del pago. Siempre en minúsculas. Mismos valores en REST (crear, listar, obtener) y en webhooks (payment.status). Los estados internos del ciclo de vida se consolidan: - pending: iniciado, validado, aceptado - processing: procesando, pendiente de procesamiento, registrando - confirmed: confirmado por el riel, aún no registrado en el ledger - posted: registrado en el ledger (se mantiene posted tras un reverso posterior) - failed: fallido, rechazado, posting_failed, cancelado, expirado Un reverso no es un estado. Usá result=reversed / has_reversal=true en REST y el webhook payment.reversed. El detalle operativo queda en status_detail (nombre interno en mayúsculas) en crear/listar/obtener de REST. El webhook payment.failed usa error.platform.code para distinguir fallos de registro de rechazos del proveedor.Valores: pending, processing, confirmed, posted, failed
payment.amountobjectsí—
payment.amount.amountstringsíMonto como texto decimal con exactamente 2 decimales (ej. "1000.00").
payment.amount.currencystringsíMoneda ISO 4217 del pago.Valores: CRC, USD
payment.client_referencestringsíReferencia del comercio enviada al crear el pago (client_reference), no detail_reference.
payment.external_referencestringsí—
payment.fee_totalstringsíTotal de comisiones como texto decimal (misma moneda que amount.currency)
payment.tax_totalstringsíTotal de impuestos como texto decimal (misma moneda que amount.currency)
payment.net_amountstringsíImpacto neto en efectivo en la cuenta del comercio. PAYIN: amount - fee_total - tax_total (lo que recibe el comercio). PAYOUT: amount + fee_total + tax_total (débito total; la comisión se cobra aparte y no se descuenta del monto transferido).
payment.succeeded_atstring (date-time)síMarca de tiempo en que el proveedor confirmó el éxito del pago.
providerobjectsíFoto compartida del proveedor en las lecturas REST de pagos y en los sobres de webhook. La referencia del riel solo está en payment.external_reference, no se duplica aquí. Los provider_status_* coinciden con rejection.reason_code / message / code del webhook cuando el proveedor rechazó la operación. null cuando no hay rechazo.
provider.correlation_idstringsíProvider transaction correlation identifier. Sent to SINPE as the correlation id. Not channel_reference (e.g. MOBILE_APP). Distinct from webhook event.correlation_id (HTTP request id for this delivery). null when unknown.Puede ser null.
provider.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
provider.provider_status_codestringsíMotivo/código crudo del proveedor (ej. 31). Mismo significado que rejection.reason_code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_descstringsíTexto Motivo del proveedor (prefiere Detalle). Mismo significado que rejection.message del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_semanticstringsíSemántica normalizada de la plataforma (ej. ACCOUNT_BLOCKED). Mismo significado que rejection.code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.

payment.failed

CampoTipoObligatorioDescripción
schema_versionstringsí—
eventobjectsí—
event.event_idstring (uuid)sí—
event.event_typestringsí—Valores: payment.failed
event.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
event.correlation_idstringsí—
contextobjectsí—
context.country_codestringsí—
context.payment_method_codestringsíCatálogo público de métodos de pago. Mismos valores que en REST (PIN, SINPE_MOVIL). Nunca se exponen códigos internos.Valores: PIN, SINPE_MOVIL
paymentobjectsí—
payment.payment_idstring (uuid)sí—
payment.public_idinteger (int64)síEs el mismo identificador: número en webhooks y texto en REST. Tratalo siempre como texto.
payment.statusstringsíCatálogo público de estados del pago. Siempre en minúsculas. Mismos valores en REST (crear, listar, obtener) y en webhooks (payment.status). Los estados internos del ciclo de vida se consolidan: - pending: iniciado, validado, aceptado - processing: procesando, pendiente de procesamiento, registrando - confirmed: confirmado por el riel, aún no registrado en el ledger - posted: registrado en el ledger (se mantiene posted tras un reverso posterior) - failed: fallido, rechazado, posting_failed, cancelado, expirado Un reverso no es un estado. Usá result=reversed / has_reversal=true en REST y el webhook payment.reversed. El detalle operativo queda en status_detail (nombre interno en mayúsculas) en crear/listar/obtener de REST. El webhook payment.failed usa error.platform.code para distinguir fallos de registro de rechazos del proveedor.Valores: pending, processing, confirmed, posted, failed
payment.amountobjectsí—
payment.amount.amountstringsíMonto como texto decimal con exactamente 2 decimales (ej. "1000.00").
payment.amount.currencystringsíMoneda ISO 4217 del pago.Valores: CRC, USD
payment.client_referencestringnoReferencia del comercio enviada al crear el pago (client_reference), no detail_reference.
payment.external_referencestringno—
payment.errorobjectsí—
payment.error.domainstringsí—Valores: provider_rejection, platform_validation, platform_posting, platform_infra, platform_reversal, sinpe_rejection
payment.error.platformobjectsí—
providerobjectsíFoto compartida del proveedor en las lecturas REST de pagos y en los sobres de webhook. La referencia del riel solo está en payment.external_reference, no se duplica aquí. Los provider_status_* coinciden con rejection.reason_code / message / code del webhook cuando el proveedor rechazó la operación. null cuando no hay rechazo.
provider.correlation_idstringsíProvider transaction correlation identifier. Sent to SINPE as the correlation id. Not channel_reference (e.g. MOBILE_APP). Distinct from webhook event.correlation_id (HTTP request id for this delivery). null when unknown.Puede ser null.
provider.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
provider.provider_status_codestringsíMotivo/código crudo del proveedor (ej. 31). Mismo significado que rejection.reason_code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_descstringsíTexto Motivo del proveedor (prefiere Detalle). Mismo significado que rejection.message del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_semanticstringsíSemántica normalizada de la plataforma (ej. ACCOUNT_BLOCKED). Mismo significado que rejection.code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
rejectionobjectnoRechazo del proveedor independiente del riel. Compartido por POST /accounts/validate (data.rejection) y el webhook payment.failed (rejection). Objeto solo cuando el proveedor rechazó la operación; omitido/null en caso contrario.
rejection.codestringsíSemántica normalizada de la plataforma derivada de reason_code. Solo un subconjunto de valores de reason_code (ver el catálogo en la descripción de POST /accounts/validate y del endpoint de contrato del webhook payment.failed) tiene entrada propia aquí; para cualquier otro reason_code numérico, code es SINPE_REJECTED aunque message sea específico (ej. reason_code = 21 "fondos insuficientes" igual devuelve code = SINPE_REJECTED, no tiene semántica propia). PHONE_NOT_REGISTERED es el valor de respaldo cuando reason_code no es numérico (flujo del monedero SINPE_MOVIL).Valores: ACCOUNT_CLOSED, ACCOUNT_NOT_FOUND, ACCOUNT_BLOCKED, INVALID_ACCOUNT, IDENT_MISMATCH, IDENT_TYPE_INVALID, INVALID_ID_FORMAT, VALIDATION_ERROR, CURRENCY_MISMATCH, NOT_AUTHORIZED_BY_RECIPIENT, PROFILE_NOT_ALLOWED, CHANNEL_NOT_RECOGNIZED, COMMUNICATION_ERROR, RETRY_LATER, WALLET_PHONE_ORIGIN_INVALID, WALLET_PHONE_ORIGIN_NOT_ENABLED, WALLET_PHONE_DEST_INVALID, WALLET_PHONE_NOT_REGISTERED_BCCR, WALLET_NOT_FOUND, WALLET_PHONE_ALREADY_ACTIVE, PHONE_NOT_REGISTERED, SINPE_REJECTED
rejection.reason_codestringsíMotivo/código crudo devuelto por SINPE. El catálogo completo está en la descripción de POST /accounts/validate (lo comparte este objeto dondequiera que aparezca, incluido el webhook payment.failed). Un código no listado igual produce respuesta: message cae a un texto genérico y code a SINPE_REJECTED.
rejection.messagestringsíTexto descriptivo del Motivo; prefiere el Detalle del proveedor cuando existe.

payment.reversed

CampoTipoObligatorioDescripción
schema_versionstringsí—
eventobjectsí—
event.event_idstring (uuid)sí—
event.event_typestringsí—Valores: payment.reversed
event.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
event.correlation_idstringsí—
contextobjectsí—
context.country_codestringsí—
context.payment_method_codestringsíCatálogo público de métodos de pago. Mismos valores que en REST (PIN, SINPE_MOVIL). Nunca se exponen códigos internos.Valores: PIN, SINPE_MOVIL
paymentobjectsí—
payment.payment_idstring (uuid)sí—
payment.public_idinteger (int64)síEs el mismo identificador: número en webhooks y texto en REST. Tratalo siempre como texto.
payment.statusstringsíCatálogo público de estados del pago. Siempre en minúsculas. Mismos valores en REST (crear, listar, obtener) y en webhooks (payment.status). Los estados internos del ciclo de vida se consolidan: - pending: iniciado, validado, aceptado - processing: procesando, pendiente de procesamiento, registrando - confirmed: confirmado por el riel, aún no registrado en el ledger - posted: registrado en el ledger (se mantiene posted tras un reverso posterior) - failed: fallido, rechazado, posting_failed, cancelado, expirado Un reverso no es un estado. Usá result=reversed / has_reversal=true en REST y el webhook payment.reversed. El detalle operativo queda en status_detail (nombre interno en mayúsculas) en crear/listar/obtener de REST. El webhook payment.failed usa error.platform.code para distinguir fallos de registro de rechazos del proveedor.Valores: pending, processing, confirmed, posted, failed
payment.resultstringsí—Valores: reversed
payment.has_reversalbooleansí—Valores: true
payment.amountobjectsí—
payment.amount.amountstringsíMonto como texto decimal con exactamente 2 decimales (ej. "1000.00").
payment.amount.currencystringsíMoneda ISO 4217 del pago.Valores: CRC, USD
payment.client_referencestringno—
payment.external_referencestringno—
payment.reversed_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
payment.reasonobjectno—
payment.reason.codestringno—
payment.reason.messagestringno—
providerobjectsíFoto compartida del proveedor en las lecturas REST de pagos y en los sobres de webhook. La referencia del riel solo está en payment.external_reference, no se duplica aquí. Los provider_status_* coinciden con rejection.reason_code / message / code del webhook cuando el proveedor rechazó la operación. null cuando no hay rechazo.
provider.correlation_idstringsíProvider transaction correlation identifier. Sent to SINPE as the correlation id. Not channel_reference (e.g. MOBILE_APP). Distinct from webhook event.correlation_id (HTTP request id for this delivery). null when unknown.Puede ser null.
provider.occurred_atstring (date-time)síMarca de tiempo RFC 3339 en UTC con precisión de segundos y sufijo Z. Nunca se emiten fracciones de segundo. Ejemplo: 2026-01-15T12:00:00Z.
provider.provider_status_codestringsíMotivo/código crudo del proveedor (ej. 31). Mismo significado que rejection.reason_code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_descstringsíTexto Motivo del proveedor (prefiere Detalle). Mismo significado que rejection.message del webhook. null cuando no hay rechazo del proveedor.Puede ser null.
provider.provider_status_semanticstringsíSemántica normalizada de la plataforma (ej. ACCOUNT_BLOCKED). Mismo significado que rejection.code del webhook. null cuando no hay rechazo del proveedor.Puede ser null.

Identificador público#

payment.public_id llega como número en el webhook y como texto en REST; es el mismo identificador. Tratalo siempre como texto.

Última verificación: 2026-10-22

Ver como Markdown crudo