Banking API accounts
API 1.0.0An MCP tool wraps this operation: Banking tools.
The Accounts group covers the account as a resource: validate a destination account
before transferring, list accessible accounts, check balances and request statements.
POST /api/public/v1/accounts/validate
GET /api/public/v1/accounts
POST /api/public/v1/accounts/balances
GET /api/public/v1/accounts/balance
POST /api/public/v1/accounts/statements
GET /api/public/v1/accounts/statements/{request_id}Resource rules#
Scope. The listing returns only the accounts the token authorizes; tenant_id and
owner_id are never sent as parameters.
Validate first. Validate the destination account before creating a payment: the operation confirms the account exists and is eligible, and returns the holder data the service exposes.
Asynchronous statements. POST /api/public/v1/accounts/statements accepts Idempotency-Key and always returns HTTP 202 (ACCEPTED) with request_id and status: PENDING; generation is asynchronous. Poll GET /api/public/v1/accounts/statements/{request_id}: it returns HTTP 200 with status PENDING, PROCESSING, DONE or ERROR. DONE includes download_url, valid for 48 hours. See
safe retries.
Pagination. GET /api/public/v1/accounts and GET /api/public/v1/accounts/balances
use limit and offset, and return pagination with limit, offset and total.
Operations#
Validate external account for a payment method
POST /api/public/v1/accounts/validate
Host: https://api-baas-sandbox.tilopay.com — requires Authorization: Bearer <access_token>.
Validates a counterparty account (IBAN or phone) via the SINPE provider for the given payment method. Does not validate internal platform accounts. When valid, data.holder.id_type uses the same identification catalog keys as identification.type in POST /payments (e.g. cedula, juridica, dimex).
SINPE_MOVIL field availability (expected, stable)
For payment_method_code = SINPE_MOVIL, the SINPE Móvil wallet query does not return identification type or institution name. A successful validation may therefore return:
data.holder.id_type=unknowndata.financial_institution.entity_name=""(empty string)
This is expected, stable behavior, not a mapping bug or a transient provider error. PIN (account query in SINPE) does populate both fields.
Integrators must not report these values as defects, and must not assume that holder.id_type or financial_institution.entity_name are always populated when is_valid is true.
SINPE rejection codes (data.rejection)
When is_valid is false, data.rejection explains why (see the rejected example on the 200 response below). rejection.code is one of a fixed set of normalized semantics (see the value table below). Most rows in the table below do NOT get a distinct `code` — only a subset of reason_code values has a dedicated semantic; every other reason_code, even one listed below with a specific message, returns code = SINPE_REJECTED (e.g. reason_code = 21 "fondos insuficientes" still returns SINPE_REJECTED). This same object and catalog is also used by the payment.failed webhook's rejection field. A reason_code not listed here at all still produces a response — message falls back to a generic text and code falls back to SINPE_REJECTED.
Account / profile / limits
| Code | Message |
|---|---|
| 21 | Cuenta Cliente con fondos insuficientes |
| 22 | Cuenta Cliente no admite créditos |
| 23 | Cuenta Cliente cerrada |
| 24 | Cuenta Cliente inactiva |
| 25 | Cuenta Cliente no admite débitos |
| 26 | Cuenta Cliente no es de fondos |
| 27 | Moneda de la Cuenta Cliente no corresponde |
| 28 | Cuenta cliente no existe |
| 29 | Cuenta Cliente no registrada en el SINPE |
| 30 | Cuenta Cliente no habilitada para el servicio |
| 31 | Cuenta Cliente bloqueada |
| 32 | Id cliente destino no coincide con registrado en la entidad |
| 33 | Nombre del cliente destino no coincide con el registrado en la entidad |
| 34 | Cuenta Cliente en proceso de cierre |
| 35 | Cuenta Cliente embargada |
| 36 | Cuenta Cliente con retención judicial |
| 37 | Cuenta de expediente simplificado no permite el monto indicado |
| 38 | Límite transaccional de la Cuenta Cliente excedido |
| 39 | Cuenta Cliente incorrecta |
| 40 | IBAN de la cuenta destino inválido |
| 41 | IBAN de la cuenta origen inválido |
| 42 | Tipo de cuenta no permite la transacción |
| 43 | Cuenta Cliente no pertenece a la entidad indicada |
| 44 | Producto de la cuenta no admite el servicio |
| 45 | Cuenta Cliente en estado de cancelación |
| 46 | Cuenta Cliente restringida por política de la entidad |
| 47 | Cuenta Cliente no permite pagos inmediatos |
| 48 | Cuenta Cliente no permite SINPE Móvil |
| 49 | Titular de la cuenta destino fallecido |
| 50 | Cuenta Cliente consolidada o migrada |
| 51 | Identificación del cliente origen no coincide |
| 52 | Identificación del cliente destino inválida |
| 53 | Cliente destino no autorizado para recibir el pago |
| 54 | Cliente origen no autorizado para enviar el pago |
| 55 | Perfil del cliente origen no permite la transacción |
| 56 | Monto inferior al mínimo permitido |
| 57 | Monto superior al máximo permitido |
| 58 | Cantidad de transacciones diarias excedida |
| 59 | Cantidad de transacciones mensuales excedida |
| 60 | Límite acumulado diario excedido |
| 61 | Límite acumulado mensual excedido |
| 62 | Comisión no pudo ser aplicada |
| 63 | Tipo de cambio no disponible |
| 64 | Transacción rechazada por control de lavado de dinero |
| 65 | Transacción rechazada por listas de control |
| 66 | Transacción en revisión de cumplimiento |
| 67 | Documento de respaldo requerido no presente |
| 68 | Firma o autenticación inválida |
| 69 | Token o segundo factor inválido |
| 70 | Sesión de usuario expirada |
| 71 | Usuario no autorizado para el canal |
| 72 | Dispositivo no registrado |
| 73 | Geolocalización no permitida |
| 74 | Operación no soportada en la moneda indicada |
| 75 | Operación no soportada para el tipo de cliente |
| 76 | Problemas de comunicación |
| 77 | Tiempo de espera agotado en la entidad origen |
| 78 | Tiempo de espera agotado en el SINPE |
| 79 | Error interno de la entidad origen |
| 80 | Error interno de la entidad destino |
| 81 | Entidad origen no disponible |
| 82 | Entidad destino no encontrada |
| 83 | Problemas en la respuesta del destino |
| 84 | Respuesta de la entidad origen incorrecta |
| 85 | Mensaje con formato electrónico inválido |
| 86 | Versión del estándar electrónico no soportada |
| 87 | Campo obligatorio no informado |
| 88 | Campo con valor fuera de catálogo |
| 89 | Checksum o integridad del mensaje inválida |
| 90 | Referencia SINPE duplicada |
| 91 | Moneda no corresponde |
| 92 | Transacción no autorizada por entidad destino |
| 93 | Transacción no autorizada por cliente destino |
| 94 | Transacción no autorizada por entidad origen |
| 95 | Transacción no autorizada por cliente origen |
| 96 | Reverso no permitido para el estado de la transacción |
| 97 | Reverso ya aplicado |
| 98 | Confirmación no permitida para el estado de la transacción |
| 99 | Liquidación no permitida para el estado de la transacción |
| 100 | Entidad Destino no disponible para procesar en tiempo real |
| 101 | Entidad origen no disponible para procesar en tiempo real |
| 102 | Servicio PIN no habilitado para la entidad destino |
| 103 | Servicio PIN no habilitado para la entidad origen |
| 104 | Código de entidad destino inválido |
| 105 | Código de entidad origen inválido |
| 106 | Código de país de la entidad destino inválido |
| 107 | Código de país de la entidad origen inválido |
| 108 | Número de referencia interna inválido |
| 109 | Número de referencia SINPE inválido |
| 110 | Transacción no se encuentra en un estado que permita la consulta |
Settlement with destination institution
| Code | Message |
|---|---|
| 201 | Tiempo respuesta excedido por la entidad destino |
| 202 | Respuesta de la entidad destino incorrecta según el estándar electrónico |
| 203 | Se recibió una excepción de la entidad destino |
| 204 | Error de comunicación con la entidad destino |
| 205 | Falló procesamiento en el SINPE |
| 206 | Transacción no autorizada por cliente destino |
| 207 | Perfil transaccional del cliente destino no permite recibir el pago |
| 208 | Operación no autorizada entre las entidades participantes |
| 209 | Pendientes de Cumplimiento |
| 210 | Conciliación de la transacción no fue posible |
Identification
| Code | Message |
|---|---|
| 801 | Identificación inválida |
| 802 | Identificación del cliente origen no encontrada |
| 803 | Identificación del cliente destino no encontrada |
| 804 | Identificación no vigente |
| 805 | Identificación vencida |
| 806 | Identificación no corresponde al tipo indicado |
| 807 | Tipo de identificación inválido |
| 808 | País de la identificación no soportado |
| 809 | Identificación de menor de edad no permitida |
| 810 | Identificación de persona jurídica no permitida para el servicio |
Format validation / Core Banking
| Code | Message |
|---|---|
| 1002 | El Id de cliente destino no cumple con el formato esperado por el SINPE |
| 1003 | El Id de cliente origen no fue informado |
| 1004 | El Id de cliente origen no cumple con el formato esperado por el SINPE |
| 1005 | Monto con formato inválido |
| 1006 | Moneda con formato inválido |
| 1007 | Problemas de comunicación con el Core Bancario |
| 1008 | Core Bancario no disponible |
| 1009 | El valor para el campo no puede ser nulo o infringir su longitud mínima o máxima |
| 1010 | El valor para el campo no corresponde al tipo de dato esperado |
| 1011 | El valor para el campo no corresponde al catálogo permitido |
| 1012 | Fecha con formato inválido |
| 1013 | Hora con formato inválido |
| 1014 | Número de referencia con formato inválido |
| 1015 | IBAN con formato inválido |
Channel / identification type
| Code | Message |
|---|---|
| 1040 | Canal no informado |
| 1041 | Canal inválido |
| 1042 | Canal no corresponde |
| 1043 | Canal no habilitado para la entidad |
| 1044 | Canal no habilitado para el servicio |
| 1045 | El formato de la identificación es inválido |
| 1046 | Tipo de identificación no informado |
| 1080 | Tipo de identificación no corresponde al cliente origen |
| 1081 | Tipo de identificación no corresponde al cliente destino |
| 1082 | Tipo de identificación no vigente |
| 1083 | Tipo de identificación no soportado por el servicio |
| 1084 | Tipo de identificación inválido |
| 1085 | Tipo de identificación no soportado por la entidad |
SINPE Móvil (wallet)
| Code | Message |
|---|---|
| 15300 | El número de teléfono origen indicado es inválido |
| 15301 | El número de teléfono origen no tiene activo el Servicio Monedero |
| 15302 | El número de teléfono destino indicado es inválido |
| 15303 | El número de teléfono destino no está registrado en el padrón móvil del BCCR |
| 15304 | No es posible inactivar el monedero indicado pues no existe |
| 15305 | El número de teléfono indicado ya se encuentra activo como monedero en el padrón local |
`rejection.code` values
| Value | Meaning |
|---|---|
ACCOUNT_CLOSED | Account closed |
ACCOUNT_NOT_FOUND | Account not found |
ACCOUNT_BLOCKED | Account blocked |
INVALID_ACCOUNT | Invalid account |
IDENT_MISMATCH | Identification does not match |
IDENT_TYPE_INVALID | Invalid identification type |
INVALID_ID_FORMAT | Invalid identification format |
VALIDATION_ERROR | Validation error |
CURRENCY_MISMATCH | Currency mismatch |
NOT_AUTHORIZED_BY_RECIPIENT | Not authorized by the recipient |
PROFILE_NOT_ALLOWED | Profile not allowed |
CHANNEL_NOT_RECOGNIZED | Channel not recognized |
COMMUNICATION_ERROR | Communication error |
RETRY_LATER | Retry later |
WALLET_PHONE_ORIGIN_INVALID | Origin phone invalid (SINPE Móvil) |
WALLET_PHONE_ORIGIN_NOT_ENABLED | Origin phone without wallet service |
WALLET_PHONE_DEST_INVALID | Destination phone invalid (SINPE Móvil) |
WALLET_PHONE_NOT_REGISTERED_BCCR | Phone not registered in the BCCR mobile registry |
WALLET_NOT_FOUND | Wallet not found |
WALLET_PHONE_ALREADY_ACTIVE | Phone already has an active wallet |
PHONE_NOT_REGISTERED | Phone not registered |
SINPE_REJECTED | Rejected by SINPE (generic fallback) |
Rate limits currently apply to authentication and account validation. Exceeding them returns HTTP 429 (TOO_MANY_REQUESTS). Retry with exponential backoff.
SINPE code 1001 means the account exists and is active; it is a valid result, not a rejection reason.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Correlation-Id | header | string | no | Optional client-supplied correlation id for end-to-end tracing.
Echoed back as correlation_id in the response envelope. If omitted,
the API generates one and still returns it. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
payment_method_code | string | yes | Payment method used to route validation (PIN for external IBAN, SINPE_MOVIL for phone wallet).Values: PIN, SINPE_MOVIL |
account | object | yes | — |
account.type | string | yes | —Values: IBAN, PHONE |
account.value | string | yes | — |
Request example
pin
{
"payment_method_code": "PIN",
"account": {
"type": "IBAN",
"value": "CR16010400000000000031"
}
}sinpe_movil
{
"payment_method_code": "SINPE_MOVIL",
"account": {
"type": "PHONE",
"value": "88887777"
}
}Response 200
The request was processed successfully.
| Field | Type | Required | Description |
|---|---|---|---|
is_valid | boolean | no | — |
account | object | no | — |
account.type | string | no | — |
account.value | string | no | — |
account.currency | string | no | — |
account.product_name | string | no | — |
holder | object | no | Account holder when validation succeeds.
id_type matches PaymentIdentification.type (same catalog as POST /payments).
For SINPE_MOVIL, id_type may be unknown because the wallet
operation does not return an identification type. That is expected
and stable; do not treat it as a bug.Can be null. |
holder.name | string | no | — |
holder.id_type | string | no | Identification catalog key (lowercase), e.g. cedula, juridica, dimex.
Same vocabulary as identification.type on payment create.
PIN: mapped from the provider account query (confirmed by the provider).
SINPE_MOVIL: the wallet query does not return an identification
type, so this field may be `unknown`. That is expected, stable
behavior due to available provider data — not a mapping bug or a
transient error. Do not assume a confirmed type is always present.Values: cedula, dimex, gobierno, juridica, institucion_autonoma, didi, pasaporte, unknown |
holder.id_number | string | no | — |
financial_institution | object | no | Counterparty institution when validation succeeds.
PIN fills entity_name from the SINPE account query.
SINPE_MOVIL only receives an entity code from the wallet query;
it does not return the institution name, so entity_name
may be `""`. That is expected, stable behavior — not a transient
error. Do not assume entity_name is always populated.Can be null. |
financial_institution.country_code | string | no | — |
financial_institution.entity_code | string | no | — |
financial_institution.entity_name | string | no | Human-readable institution name.
PIN: populated from the provider account-info operation.
SINPE_MOVIL: may be "" because the wallet query does not
expose the name. Expected limitation, not an error. |
rejection | object | no | Present when is_valid is false and the provider rejected the account. null when is_valid is true.Can be null. |
rejection.code | string | yes | Normalized platform semantic derived from reason_code. Only a subset of
reason_code values (see the catalog in the description of POST /accounts/validate
and of the payment.failed webhook contract endpoint) has a distinct entry here —
for any other numeric reason_code, code is SINPE_REJECTED even when message
is specific (e.g. reason_code = 21 "fondos insuficientes" still returns
code = SINPE_REJECTED, it has no dedicated semantic). PHONE_NOT_REGISTERED is
the fallback when reason_code is non-numeric (SINPE_MOVIL wallet flow).Values: 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_code | string | yes | Raw Motivo/code returned by SINPE. See the endpoint description of
POST /accounts/validate for the full code catalog (shared by this object
wherever it appears, including the payment.failed webhook). A code not
listed there still produces a response — message falls back to a generic
text and code falls back to SINPE_REJECTED. |
rejection.message | string | yes | Descriptive Motivo text; prefers provider Detalle when present. |
provider | object | no | — |
provider.operation | string | no | — |
provider.correlation_id | string | no | —Can be null. |
provider.occurred_at | string (date-time) | no | —Can be null. |
Response example — valid
{
"success": true,
"http_status_code": 200,
"response_code": "OK",
"message": "The request was processed successfully.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"is_valid": true,
"account": {
"type": "IBAN",
"value": "CR16010400000000000031",
"currency": "CRC",
"product_name": "Cuenta corriente"
},
"holder": {
"name": "Juan Perez",
"id_type": "cedula"
},
"financial_institution": {
"entity_code": "104",
"entity_name": "Banco Ejemplo"
},
"provider": {
"reason_code": "0",
"message": "Cuenta válida",
"code": null
},
"rejection": null
}
}Response example — rejected
{
"success": true,
"http_status_code": 200,
"response_code": "OK",
"message": "The request was processed successfully.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"is_valid": false,
"account": {
"type": "IBAN",
"value": "CR16010400000000000099",
"currency": "CRC",
"product_name": ""
},
"holder": null,
"financial_institution": {
"entity_code": "104",
"entity_name": "Banco Ejemplo"
},
"provider": {
"reason_code": "28",
"message": "Cuenta cliente no existe",
"code": "ACCOUNT_NOT_FOUND"
},
"rejection": {
"code": "ACCOUNT_NOT_FOUND",
"reason_code": "28",
"message": "Cuenta cliente no existe"
}
}
}Error responses
| HTTP | response_code | Description |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid request. Check the required fields and try again. |
| 400 | INVALID_ACCOUNT_TYPE | Unsupported or invalid account type. |
| 400 | INVALID_ACCOUNT_FORMAT | Invalid account format. |
| 400 | INVALID_PAYMENT_METHOD | Invalid or unsupported payment method. |
| 401 | UNAUTHORIZED | Unauthorized. Verify your session or credentials. |
| 403 | FORBIDDEN | You do not have permission to perform this action. |
| 403 | ACCOUNT_ACCESS_DENIED | The actor does not have access to the requested account. |
| 422 | UNPROCESSABLE_ENTITY | The request is well-formed but cannot be processed due to business rules. |
| 429 | TOO_MANY_REQUESTS | Too many requests. Please retry after a short delay. |
| 500 | INTERNAL_ERROR | An unexpected error occurred. Please try again later. |
| 502 | PROVIDER_ERROR | Unable to complete the request with the payment provider. Please try again later. |
List balances for accessible accounts
GET /api/public/v1/accounts/balances
Host: https://api-baas-sandbox.tilopay.com — requires Authorization: Bearer <access_token>.
Returns paginated account balances (account, amounts, as_of). This endpoint checks the current balance in real time and may respond more slowly than GET /accounts. For account attributes without amounts, use GET /accounts.
Optional filters use the public account identification standard: type + value (identifier scheme + value). type is not the internal account classification.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Correlation-Id | header | string | no | Optional client-supplied correlation id for end-to-end tracing.
Echoed back as correlation_id in the response envelope. If omitted,
the API generates one and still returns it. |
limit | query | integer | no | — |
offset | query | integer | no | — |
type | query | string | no | Optional filter by account identifier scheme (e.g. IBAN).
Not the internal account classification. |
value | query | string | no | Optional filter by account identifier value (partial match supported for list endpoints). |
Response 200
The request was processed successfully.
| Field | Type | Required | Description |
|---|---|---|---|
items | array<object> | no | — |
items[].account | object | yes | — |
items[].account.type | string | yes | Account identifier scheme (e.g. IBAN, PHONE).
Not the internal account classification (e.g. operational).Values: IBAN, PHONE |
items[].account.value | string | yes | When type is PHONE (SINPE Móvil): 8 local digits, no country prefix (e.g. "88887777"). |
items[].account.currency | string | no | Optional account currency in response payloads. |
items[].amounts | object | yes | Ledger balances as decimal strings with 2 fraction digits (same display scale as public payment amounts). Prefer string over JSON number for financial amounts. |
items[].amounts.available | string | no | — |
items[].amounts.posted | string | no | — |
items[].amounts.inbound_in_transit | string | no | — |
items[].amounts.outbound_in_transit | string | no | — |
items[].as_of | string (date-time) | yes | Ledger timestamp when known (RFC 3339 UTC, second precision, Z); null when not available.Can be null. |
pagination | object | no | — |
pagination.limit | integer | no | — |
pagination.offset | integer | no | — |
pagination.total | integer | no | — |
Response example
{
"success": true,
"http_status_code": 200,
"response_code": "OK",
"message": "The request was processed successfully.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"items": [
{
"account": {
"type": "IBAN",
"value": "CR26036900101010588061",
"currency": "CRC"
},
"amounts": {
"available": "991900.00",
"posted": "991900.00",
"inbound_in_transit": "0.00",
"outbound_in_transit": "0.00"
},
"as_of": "2026-01-15T12:00:00Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1
}
}
}Error responses
| HTTP | response_code | Description |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid request. Check the required fields and try again. |
| 401 | UNAUTHORIZED | Unauthorized. Verify your session or credentials. |
| 403 | FORBIDDEN | You do not have permission to perform this action. |
| 403 | ACCOUNT_ACCESS_DENIED | The actor does not have access to the requested account. |
| 404 | NOT_FOUND | The requested resource was not found. |
| 500 | INTERNAL_ERROR | An unexpected error occurred. Please try again later. |
| 502 | — | Dependency error |
| 503 | SERVICE_UNAVAILABLE | A required service is temporarily unavailable. Please try again later. |
List accessible accounts without balances
GET /api/public/v1/accounts
Host: https://api-baas-sandbox.tilopay.com — requires Authorization: Bearer <access_token>.
Returns paginated accessible accounts without querying current balances. Each item is account attributes only (account.type, account.value, account.currency). amounts and as_of are not included; use GET /accounts/balances or POST /accounts/balance.
Optional filters use the public account identification standard: type + value (identifier scheme + value). type is not the internal account classification.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Correlation-Id | header | string | no | Optional client-supplied correlation id for end-to-end tracing.
Echoed back as correlation_id in the response envelope. If omitted,
the API generates one and still returns it. |
limit | query | integer | no | — |
offset | query | integer | no | — |
type | query | string | no | Optional filter by account identifier scheme (e.g. IBAN).
Not the internal account classification. |
value | query | string | no | Optional filter by account identifier value (partial match supported for list endpoints). |
Response 200
The request was processed successfully.
| Field | Type | Required | Description |
|---|---|---|---|
items | array<object> | no | — |
items[].account | object | yes | — |
items[].account.type | string | yes | Account identifier scheme (e.g. IBAN, PHONE).
Not the internal account classification (e.g. operational).Values: IBAN, PHONE |
items[].account.value | string | yes | When type is PHONE (SINPE Móvil): 8 local digits, no country prefix (e.g. "88887777"). |
items[].account.currency | string | no | Optional account currency in response payloads. |
pagination | object | no | — |
pagination.limit | integer | no | — |
pagination.offset | integer | no | — |
pagination.total | integer | no | — |
Response example
{
"success": true,
"http_status_code": 200,
"response_code": "OK",
"message": "The request was processed successfully.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"items": [
{
"account": {
"type": "IBAN",
"value": "CR26036900101010588061",
"currency": "CRC"
}
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1
}
}
}Error responses
| HTTP | response_code | Description |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid request. Check the required fields and try again. |
| 401 | UNAUTHORIZED | Unauthorized. Verify your session or credentials. |
| 403 | FORBIDDEN | You do not have permission to perform this action. |
| 404 | NOT_FOUND | The requested resource was not found. |
| 500 | INTERNAL_ERROR | An unexpected error occurred. Please try again later. |
| 502 | — | Dependency error |
| 503 | SERVICE_UNAVAILABLE | A required service is temporarily unavailable. Please try again later. |
Get account balance
POST /api/public/v1/accounts/balance
Host: https://api-baas-sandbox.tilopay.com — requires Authorization: Bearer <access_token>.
Returns a single account snapshot with amounts and as_of. For account attributes without balances, use GET /accounts.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Correlation-Id | header | string | no | Optional client-supplied correlation id for end-to-end tracing.
Echoed back as correlation_id in the response envelope. If omitted,
the API generates one and still returns it. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
account | object | yes | — |
account.type | string | yes | Account identifier scheme. Only IBAN is supported here.
This is not the internal account classification
(e.g. operational).Values: IBAN |
account.value | string | yes | IBAN (spaces allowed; normalized server-side) |
Response 200
The request was processed successfully.
| Field | Type | Required | Description |
|---|---|---|---|
account | object | yes | — |
account.type | string | yes | Account identifier scheme (e.g. IBAN, PHONE).
Not the internal account classification (e.g. operational).Values: IBAN, PHONE |
account.value | string | yes | When type is PHONE (SINPE Móvil): 8 local digits, no country prefix (e.g. "88887777"). |
account.currency | string | no | Optional account currency in response payloads. |
amounts | object | yes | Ledger balances as decimal strings with 2 fraction digits (same display scale as public payment amounts). Prefer string over JSON number for financial amounts. |
amounts.available | string | no | — |
amounts.posted | string | no | — |
amounts.inbound_in_transit | string | no | — |
amounts.outbound_in_transit | string | no | — |
as_of | string (date-time) | yes | Ledger timestamp when known (RFC 3339 UTC, second precision, Z); null when not available.Can be null. |
Response example
{
"success": true,
"http_status_code": 200,
"response_code": "OK",
"message": "The request was processed successfully.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"account": {
"type": "IBAN",
"value": "CR26036900101010588061",
"currency": "CRC"
},
"amounts": {
"available": "991900.00",
"posted": "991900.00",
"inbound_in_transit": "0.00",
"outbound_in_transit": "0.00"
},
"as_of": "2026-01-15T12:00:00Z"
}
}Error responses
| HTTP | response_code | Description |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid request. Check the required fields and try again. |
| 401 | UNAUTHORIZED | Unauthorized. Verify your session or credentials. |
| 403 | FORBIDDEN | You do not have permission to perform this action. |
| 403 | ACCOUNT_ACCESS_DENIED | The actor does not have access to the requested account. |
| 404 | ACCOUNT_NOT_FOUND | Account not found. |
| 422 | ACCOUNT_NOT_CONFIGURED | This account is not mapped in the system. Please contact support to configure it correctly. |
| 422 | UNPROCESSABLE_ENTITY | The request is well-formed but cannot be processed due to business rules. |
| 500 | INTERNAL_ERROR | An unexpected error occurred. Please try again later. |
| 502 | PROVIDER_ERROR | Unable to complete the request with the payment provider. Please try again later. |
| 503 | SERVICE_UNAVAILABLE | A required service is temporarily unavailable. Please try again later. |
Request account statement generation
POST /api/public/v1/accounts/statements
Host: https://api-baas-sandbox.tilopay.com — requires Authorization: Bearer <access_token>. Accepts Idempotency-Key.
Creates an asynchronous account statement request for a date range (max 60 days). Identify the account with account.type + account.value (identifier scheme + value, e.g. IBAN). This follows the public account identification standard. account.type is not the internal account classification. Returns {request_id, status: PENDING} immediately. Poll GET /accounts/statements/{request_id} for status; when DONE, the response includes a signed download_url.
Always returns HTTP 202 (ACCEPTED) with request_id and status PENDING. Generation is asynchronous. Poll GET /api/public/v1/accounts/statements/{request_id}: HTTP 200 with PENDING, PROCESSING, DONE or ERROR. DONE includes download_url, valid for 48 hours.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Correlation-Id | header | string | no | Optional client-supplied correlation id for end-to-end tracing.
Echoed back as correlation_id in the response envelope. If omitted,
the API generates one and still returns it. |
Idempotency-Key | header | string | no | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
account | object | yes | — |
account.type | string | yes | Account identifier scheme. Only IBAN is supported here.
This is not the internal account classification
(e.g. operational).Values: IBAN |
account.value | string | yes | IBAN (spaces allowed; normalized server-side) |
date_from | string (date-time) | yes | RFC 3339 timestamp accepted on requests. UTC Z or a numeric offset is allowed.
Optional fractional seconds are accepted and truncated to seconds. |
date_to | string (date-time) | yes | RFC 3339 (max 60 days after date_from) |
notify_email | boolean | no | — |
Response 202
Statement request accepted for asynchronous generation.
response_code: ACCEPTED
| Field | Type | Required | Description |
|---|---|---|---|
request_id | string (uuid) | yes | — |
status | string | yes | —Values: PENDING |
Response example
{
"success": true,
"http_status_code": 202,
"response_code": "ACCEPTED",
"message": "Statement request accepted.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"request_id": "5f3b2c1d-9e8a-4b7c-8d6e-5f4a3b2c1d0e",
"status": "PENDING"
}
}Error responses
| HTTP | response_code | Description |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid request. Check the required fields and try again. |
| 401 | UNAUTHORIZED | Unauthorized. Verify your session or credentials. |
| 403 | FORBIDDEN | You do not have permission to perform this action. |
| 403 | ACCOUNT_ACCESS_DENIED | The actor does not have access to the requested account. |
| 404 | NOT_FOUND | The requested resource was not found. |
| 409 | IDEMPOTENCY_CONFLICT | Idempotency conflict: the same Idempotency-Key was reused with a different request body, or a previous request is still in progress. |
| 500 | INTERNAL_ERROR | An unexpected error occurred. Please try again later. |
| 502 | — | Dependency error |
| 503 | SERVICE_UNAVAILABLE | A required service is temporarily unavailable. Please try again later. |
Get account statement request status
GET /api/public/v1/accounts/statements/{request_id}
Host: https://api-baas-sandbox.tilopay.com — requires Authorization: Bearer <access_token>.
Returns request status. When DONE, includes a signed, time-limited download_url (48h from generation) and expires_at. When ERROR, includes a generic message without internal details. Unknown or unauthorized request_id returns 404.
Returns HTTP 200 with status PENDING, PROCESSING, DONE or ERROR. DONE includes download_url, valid for 48 hours.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Correlation-Id | header | string | no | Optional client-supplied correlation id for end-to-end tracing.
Echoed back as correlation_id in the response envelope. If omitted,
the API generates one and still returns it. |
request_id | path | string (uuid) | yes | — |
Response 200
The request was processed successfully.
| Field | Type | Required | Description |
|---|---|---|---|
request_id | string (uuid) | yes | — |
status | string | yes | —Values: PENDING, PROCESSING, DONE, ERROR |
download_url | string (uri) | no | Signed download URL, present when status is DONE; valid for 48 hours from generation. |
expires_at | string (date-time) | no | Expiration of download_url (48h from generation) |
message | string | no | Generic error message when status is ERROR |
Response example
{
"success": true,
"http_status_code": 200,
"response_code": "OK",
"message": "The request was processed successfully.",
"correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"data": {
"request_id": "5f3b2c1d-9e8a-4b7c-8d6e-5f4a3b2c1d0e",
"status": "DONE",
"download_url": "https://storage.example.com/statements/5f3b2c1d.pdf?signature=example",
"expires_at": "2026-01-17T12:00:00Z",
"message": null
}
}Error responses
| HTTP | response_code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Unauthorized. Verify your session or credentials. |
| 403 | FORBIDDEN | You do not have permission to perform this action. |
| 404 | NOT_FOUND | The requested resource was not found. |
| 500 | INTERNAL_ERROR | An unexpected error occurred. Please try again later. |
| 503 | SERVICE_UNAVAILABLE | A required service is temporarily unavailable. Please try again later. |
Validation and SINPE codes#
Code 1001 means the account exists and is active: it is not a rejection. 208 means “Operation not authorized between the participating institutions” and 209, “Pending compliance”.
Account validation is rate-limited: exceeding the limit returns 429 (TOO_MANY_REQUESTS). Retry with exponential backoff.
Last verified: 2026-10-22