# Tilopay Developer and AI Agent Portal — full text > Tilopay technical documentation (SDK, API, webhooks, testing) in Markdown, with nothing normative locked inside images. Language: English. Index: https://tilopay.com/en/llms.txt ## Machine-readable artifacts - [openapi.json](https://tilopay.com/developers/openapi.json): OpenAPI 3.1 spec of the Tilopay API, source of truth for endpoints, parameters and responses. - [mcp.json](https://tilopay.com/developers/mcp.json): Catalog of the tools of the Tilopay MCP server, with parameters, outputs and access level. - [api-bancario/openapi.json](https://tilopay.com/developers/api-bancario/openapi.json): OpenAPI 3.0.3 spec of the Tilopay Banking API (BaaS), version 1.0.0, a separate artifact from the acquiring API. - [skills/index.md](https://tilopay.com/en/developers/skills/index.md) - [cierre-de-caja/SKILL.md](https://tilopay.com/developers/skills/cierre-de-caja/SKILL.md) - [cobrar-por-whatsapp/SKILL.md](https://tilopay.com/developers/skills/cobrar-por-whatsapp/SKILL.md) - [rechazos-y-recuperacion/SKILL.md](https://tilopay.com/developers/skills/rechazos-y-recuperacion/SKILL.md) - [conciliacion-mensual/SKILL.md](https://tilopay.com/developers/skills/conciliacion-mensual/SKILL.md) - [cfo-de-bolsillo/SKILL.md](https://tilopay.com/developers/skills/cfo-de-bolsillo/SKILL.md) - [catalogo-y-cobros-fijos/SKILL.md](https://tilopay.com/developers/skills/catalogo-y-cobros-fijos/SKILL.md) --- # API · Authentication > How to get the API token and the SDK token, how long each lasts, and how to send them on every call. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/api/autenticacion The whole API and the SDK live on a single host: `https://app.tilopay.com`. There is no separate host for testing — see [environments](/developers/entornos). ## The two operations [#operaciones] | Operation | Path | What for | |---|---|---| | API token | `POST /api/v1/login` | Server-to-server API calls | | SDK token | `POST /api/v1/loginSdk` | The token passed to `Tilopay.Init()` | `loginSdk` is the method the previous article called "GetTokenSdk". It is the same one. ## Get the API token [#token-api] ```bash curl -X POST https://app.tilopay.com/api/v1/login \ -H "Content-Type: application/json" \ -d '{ "apiuser": "YOUR_APIUSER", "password": "YOUR_PASSWORD" }' ``` ### Body [#cuerpo] The merchant API user. The merchant API password. ### Response [#respuesta] The token to send on subsequent calls. Always `bearer`. Token lifetime. The exact value comes in this response: do not assume it on the client. ## Get the SDK token [#token-sdk] ```bash curl -X POST https://app.tilopay.com/api/v1/loginSdk \ -H "Content-Type: application/json" \ -d '{ "apiuser": "YOUR_APIUSER", "password": "YOUR_PASSWORD" }' ``` The resulting token is the one passed to the `token` parameter of [`Tilopay.Init()`](/developers/sdk/reference/init). ## Lifetime [#vigencia] - **API token: 24 hours.** - **SDK token: 1 hour.** The exact value always arrives in `expires_in`. Cache it server-side and renew on expiry. ## How it is sent [#envio] ```http Authorization: bearer ``` ## Revocation and limits [#revocacion] - **Tokens cannot be revoked.** If a token leaks there is no operation to invalidate it: treat the leak as an incident and rotate credentials with Tilopay. - **There is no limit** on concurrent tokens per merchant. You can request one per process without invalidating the previous ones. ## Where credentials come from [#credenciales] `apiuser`, `password` and `key` are obtained in the merchant panel at `admin.tilopay.com/admin/checkout`. If you do not have access yet, start with [developer registration](/developers/registro). Never put `apiuser` or `password` in the browser. The login runs on your server; only the SDK token reaches the browser. --- # Hosted payment page > The integration path where the customer pays on a page hosted by Tilopay and your server receives the result. - kind: guide - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/hosted-payment-page ## What it is [#que-es] Your server requests a payment URL from the API, redirects the customer to that Tilopay page and receives the result on your callback URL. Card data is typed on a Tilopay page: **you never see a card number** and your merchant server stays out of that flow. The API operation for this path lives in [API · processPayment](/developers/api/hosted-payment-page/process-payment). ## Difference from a payment link [#diferencia-link] - **Hosted payment page**: your server requests a payment URL via API and redirects the customer to it. Best for integrated checkout flows in your site or app. - **Payment link**: a reusable URL created via API or the dashboard and shared by message, email, social media or QR. The customer pays when convenient; no redirect from your flow is needed. - In short: HPP for integrated, one-time checkout; payment links for shared, low-front-end charges. ## Who it is for [#para-quien] For teams that take payments with their own code but prefer not to build or maintain the card form. What you need is backend work: request the URL and handle the callback. ## What you need before starting [#requisitos] 1. **Your own credentials.** There are no shared sandbox credentials: every developer gets theirs through [developer registration](/developers/registro). 2. **API authentication.** See [authentication](/developers/api/autenticacion). 3. **A callback URL of yours** that can receive the transaction result. ## How to confirm the state of a charge [#confirmacion] The result arriving on your callback is not the only signal available, and not every error response travels with an HTTP 4xx: - Read [how to read an error response](/developers/api/procesos-operativos/errors) before declaring success. - Configure [webhooks](/developers/api/webhooks) for the events you care about. - If a call fails without a clear response, resolve the state with [safe retries](/developers/reintentos-seguros). ## Environments [#entornos] Testing and production share the same host. See [environments](/developers/entornos) before taking real money. ## Compliance [#cumplimiento] Together with the no-code path, this is the one that exposes your infrastructure the least. Confirm the applicable scope with your compliance team. ## If you prefer the form on your own site [#alternativa] Use the [JavaScript SDK](/developers/sdk): the design is yours and the data still does not pass through your server. --- # Environments: testing and production > Testing and production share one host. The mode is switched from the account and verified with the test field returned by Init(). - kind: concept - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/entornos **There is no sandbox host.** Testing and production use the same host: `app.tilopay.com`. The URL does not tell you which mode you are in. ## How the mode is switched [#como-se-cambia] The mode is switched **from the account in the Tilopay portal**, not from the integration. There is no parameter, header or alternate URL that changes it from code. ## How to verify it [#como-se-verifica] `Tilopay.Init()` returns a **`test`** field: | Value | Meaning | |---|---| | `0` | Production | | `1` | Testing | That is the only signal available at runtime. Read it and surface it in your own checkout while developing. ## The real risk [#riesgo] Because the host is the same, an integrator can believe they are testing while actually being in production, taking real money. **Verify `test` before assuming the mode.** If your integration moves money, make an unexpected `test: 0` fail loudly in your development environments. ## Test cards [#tarjetas] In testing mode you charge with the [test cards](/en/developers/tarjetas-de-prueba): each one lists the number, brand, expected result and whether it opens the 3DS challenge screen. --- # Test cards > The 27 cards of the Tilopay testing environment, with the expected result and the 3DS friction of each one. - kind: guide - status: stable - api_version: v1 - last_verified: 2026-09-03 - url: https://tilopay.com/en/developers/tarjetas-de-prueba These cards only work when the account is in **testing mode**. The mode is switched from the account and verified with the `test` field returned by `Init()`: see [environments and testing](/en/developers/entornos). ## How to use them [#uso] | Field | What to enter | | --- | --- | | Expiry date | Any date in the future | | CVV | Any value | | Code on the 3DS challenge screen | `3ds2` | On the 3DS challenge screen the code to enter is **`3ds2`**. There is no other code and nothing is sent over SMS in testing. ## Cards [#tarjetas] The brand comes from the prefix: `4` Visa, `51`–`55` Mastercard, `34` American Express. | Number | Brand | Expected result | Friction | | --- | --- | --- | --- | | `4111111111111111` | Visa | Approved | No friction | | `4012000000020089` | Visa | Approved | No friction | | `4012000000020006` | Visa | Approved | No friction | | `4012010000020088` | Visa | Approved | No friction | | `5100270000000072` | Mastercard | Approved | No friction | | `5100270000000031` | Mastercard | Approved | No friction | | `341111000000009` | American Express | Approved | No friction | | `341111000000011` | American Express | Approved | No friction | | `341112000000001` | American Express | Approved | No friction | | `341111000000037` | American Express | Approved | No friction | | `341112000008012` | American Express | Approved | No friction | | `4012000000020071` | Visa | Approved | 3DS challenge | | `4012010000020070` | Visa | Approved | 3DS challenge | | `4012010000020005` | Visa | Approved | 3DS challenge | | `5100270000000023` | Mastercard | Approved | 3DS challenge | | `5100271000000120` | Mastercard | Approved | 3DS challenge | | `4112613451591116` | Visa | Insufficient funds | 3DS challenge | | `4523080346468525` | Visa | Incorrect CVV | No friction | | `4549179990476733` | Visa | Restricted card | No friction | | `4012000000020121` | Visa | Declined | No friction | | `4111111111119999` | Visa | Declined | No friction | | `4111111111110000` | Visa | Declined | No friction | | `5100270000000056` | Mastercard | Declined | No friction | | `5111111111113333` | Mastercard | Declined | No friction | | `4666666666662222` | Visa | Declined | No friction | | `5555666666662222` | Mastercard | Declined | No friction | | `5100270000000098` | Mastercard | Declined | 3DS challenge | Cards with **3DS challenge** friction open the authentication screen; **No friction** cards resolve without it. The expected result is always the same: the card determines the response, not the amount. --- # Safe retries and orderNumber > The API is not idempotent. Exactly what to do when a payment call dies on a timeout. - kind: concept - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/reintentos-seguros **The Tilopay API is not idempotent.** There is no idempotency key and no retry that returns the original transaction. If you come from other processors, this is the assumption to dismantle before writing code. ## `orderNumber` is unique forever [#ordernumber] `orderNumber` is unique per merchant **across their entire operation**, forever. It does not reset daily, monthly or seasonally. If it repeats, **the new transaction is rejected** with the response **"Transacción duplicada"** (duplicate transaction). It does not return the original transaction: it returns a rejection. ## The timeout problem [#timeout] When a payment call dies on a network error or timeout, you do not know whether the transaction was created. And neither intuitive way out works: | What you would do | What happens | |---|---| | Retry with the same `orderNumber` | Duplicate rejection, even if the first one went through | | Retry with a new `orderNumber` | Risk of double charge | **On a network timeout there is no safe retry.** ## What to do instead [#que-hacer] Query the state before deciding: 1. Call `POST /api/v1/consult` with the original `orderNumber`. 2. If the transaction exists, use its result. Do not retry. 3. If it does not exist, then you can retry — and you can reuse the same `orderNumber`, because it was not consumed. ```text payment → timeout │ └─→ consult(original orderNumber) ├─ exists → use that result └─ not found → retry ``` ## Design consequences [#diseno] - Generate the `orderNumber` in your system **before** calling the payment and persist it. If you generate it on the fly, after a timeout you have nothing to query with. - Never derive the `orderNumber` from something that can repeat (recycled cart number, truncated timestamp, resettable counter). - A "Transacción duplicada" does not mean the charge failed: it means that `orderNumber` was already used. Query before showing the customer an error. --- # API · How to read an error response > An HTTP 200 does not mean success. Where the issuer decline arrives and how to write error handling. - kind: concept - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/api/procesos-operativos/errors ## The issuer decline [#rechazo] When the issuer declines a transaction, the response carries two fields: The issuer decline code. The decline text. ## An HTTP 200 does not mean success [#estado-http] **There is no defined standard for when the API responds HTTP 4xx and when it responds HTTP 200 with the error in the body.** Both forms coexist, and there are **four different error envelopes depending on the endpoint**. Practical consequence: your error handling must always look at the body, not only the status code, and tolerate more than one body shape. ## How to write error handling [#como-manejar] - Treat `code` and `description` as data to log and display, not as an enumeration to branch on. - Do not write `if (status === 200) success`. Check the body. - Store the raw response of every decline: it is what lets you reconstruct your merchant's decline pattern. - To determine the real state of a transaction after a failure, use [safe retries](/developers/reintentos-seguros). --- # API · Four integration paths > The four ways to take payments with Tilopay, and the difference that decides which one fits you: where the card data travels. - kind: concept - status: stable - api_version: v1 - last_verified: 2026-10-10 - url: https://tilopay.com/en/developers/api/caminos-de-integracion ## Four paths [#cuatro-caminos] There are four ways to take payments with Tilopay. They differ in two things: how much you build yourself, and where the card fields appear. Pick one and follow its guide; all of them start with your sandbox credentials, requested at [developer registration](/en/developers/registro). ### 1 · No code [#sin-codigo] Extensions and plugins for existing platforms. You do not write a line of code. [See path](/en/developers/sin-codigo) ### 2 · Hosted payment page [#hosted-payment-page] Your server requests a URL from the API, redirects the customer to that Tilopay page and receives the result on your callback URL. [See path](/en/developers/hosted-payment-page) ### 3 · JavaScript SDK [#sdk-javascript] The card fields live in your page; the SDK captures them and sends them straight to Tilopay. [See path](/en/developers/sdk) ### 4 · Server-to-server API [#servidor-a-servidor] Restricted access: a service exclusive to PCI-certified merchants. The URL is provisioned per merchant and is not public. [Requirements and request](/en/developers/server-to-server) ## How to choose [#como-elegir] Choosing the wrong path is expensive. The difference that matters most is where the card data travels. | Path | Who builds the form | Where card data travels | What you need to know | Compliance implication | | --- | --- | --- | --- | --- | | 1 · No code | The platform | Never pass through your systems | Setup inside the platform admin | Check with your compliance team | | 2 · Hosted payment page | Tilopay | Your customer types them on a secure Tilopay page; you never see a card number | Backend: request the URL and handle the callback | Check with your compliance team | | 3 · JavaScript SDK | Your site | Typed on your page but sent from the browser straight to Tilopay; they do not pass through your server | JavaScript frontend, plus backend for credentials | Check with your compliance team | | 4 · Server-to-server (restricted access) | Your site | Card data passes through your server | Requires your PCI certification and a URL provisioned by Tilopay | Check with your compliance team | Path 4 is not self-serve: it is exclusive to merchants holding PCI certification, and the URL is provisioned individually. It is also the only path that puts your server inside PCI DSS scope. Paths 2 and 3 avoid that cost and you can start them today. ## After you choose [#despues-de-elegir] With the path picked, continue with [response conventions](/en/developers/api/convenciones) to learn how amounts are typed, and [how to read an error response](/en/developers/api/procesos-operativos/errors) before you put the code into production. --- # API · Response conventions > How amounts are typed, where a JSON payload hides inside a string and why the same entity changes shape between fetching one and listing many. - kind: concept - status: stable - api_version: v1 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api/convenciones The acquiring API responses have three details that you cannot infer by reading each operation on its own, and that break typed integrations. They live here because they apply to several operations at once. For error handling, see [how to read an error response](/developers/api/procesos-operativos/errors). ## Amounts come as strings, not numbers [#montos-cadena] Amounts, discounts and totals are serialized as strings: `"discount": "40.00"`, `"amount": "0.00"`, `"first_amount": "0.00"`, `"amount_max_daily": "1000.00"`. Any typed deserialization that declares those fields as numbers fails. Declare them as strings and convert them yourself, with your language's decimal type, not with floating point. It shows up, among others, in [get a coupon](/developers/api/recurrentes/cupones/get-coupon), [list coupons](/developers/api/recurrentes/cupones/get-coupons), [list plans](/developers/api/recurrentes/planes/get-plans) and [subscriber payments](/developers/api/recurrentes/suscriptores/suscriptor-payments). ## There is a JSON payload inside a string [#doble-encoding] In [subscriber payments](/developers/api/recurrentes/suscriptores/suscriptor-payments), the `data` field of each payment **is not an object: it is a string containing JSON**. ```json "data": "{\"first\":1,\"expire\":\"2024-12-28T19:05:29.572648Z\",\"amount\":0,\"activeAmount\":0,\"originalAmount\":0,\"key\":\"XXXX-XXXX-XXXX-XXXX-XXXX\"}" ``` You have to deserialize twice: first the response, then the contents of `data`. Treat that second deserialization the way you treat any external input: wrap it and log the raw string when it does not parse. ## The same entity changes shape between "get one" and "list" [#uno-vs-lista] This is not a documentation slip: the two operations return different shapes of the same coupon, and each needs its own mapping. | | [`getCoupon`](/developers/api/recurrentes/cupones/get-coupon) (one) | [`getRepeatCoupons`](/developers/api/recurrentes/cupones/get-coupons) (list) | | --- | --- | --- | | Creation date | `created_at`, ISO format | `create`, `Y-m-d H:i:s` format | | Other fields | includes `recurrent_id`, `updated_at` and `deleted_at` | does not include them | The same happens in [subscriber payments](/developers/api/recurrentes/suscriptores/suscriptor-payments), which also uses `create` with the `Y-m-d H:i:s` format. ## Two ways of saying "not configured" [#no-configurado] In [list plans](/developers/api/recurrentes/planes/get-plans), a plan's webhook fields come as an empty string when they are not configured, while `return_url` comes as `null`. Those are two representations of the same state in the same object: when checking whether a webhook is configured, treat the empty string and `null` as the same case. --- # API · Get API token > API login operation: returns the bearer token that signs every other call. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/autenticacion/token-api ```http POST /api/v1/login ``` ## What it does [#que-hace] Returns the security token needed to consume the API services. The API user and password come from [Admin · Tilopay Checkout](https://admin.tilopay.com/admin/checkout). ## Authentication [#autenticacion] It does not require a token: this is the operation that issues it. See [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Merchant API user, obtained in Admin · Tilopay Checkout. Merchant API password, obtained in Admin · Tilopay Checkout. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "apiuser": "", "password": "" } ``` ## Response [#respuesta] ```json { "access_token": "", "token_type": "bearer", "expires_in": 86400 } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · processPayment > Parameters, request example and response of processPayment, the operation that opens the payment form. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/hosted-payment-page/process-payment ```http POST /api/v1/processPayment ``` ## What it does [#que-hace] Returns the URL of the payment form for a purchase. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Site where you wait for the transaction response, for example `mywebsite.com`. It must support the **GET** method. Example response to the redirect: `mywebsite.com?code=1&description=Transaction%20is%20approved.&auth=123456&order=TPYS-881WROIBQA1405468&tpt=4300&crd=&tilopay-transaction=4300&OrderHash=b02927cb...&returnData=Tilopay-dataReturn123456789&form_update=ok`. When `code = 1` the transaction is approved; anything else is declined. `OrderHash` is a unique string per transaction: to use it on the merchant side, write to `soporte@tilopay.com` for instructions. Key associated with the customer. It is obtained in Admin · Tilopay Checkout. Purchase amount. Purchase currency in ISO format, for example USD, CRC, GTQ. Order number, it can be alphanumeric. Capture and authorize: 1 yes, 0 no. First name. Last names. Address. Address 2. City. State in ISO format, for example CR-SJ (San José, Costa Rica) or US-CA (California, USA). Post code. Country in ISO Alpha-2 code, for example CR (Costa Rica), US (USA) or GT (Guatemala). Telephone. Buyer email. First name. Last names. Address. Address 2. City. State in ISO format, for example CR-SJ (San José, Costa Rica) or US-CA (California, USA). Post code. Country in ISO Alpha-2 code, for example CR (Costa Rica), US (USA) or GT (Guatemala). Telephone. 1 to force the customer to save the card in Tilopay, 0 not to. Name of the platform where the transaction is generated. Value that Tilopay returns in the final response. Sending a base 64 string is recommended. Only used when implementing hash verification. Possible values [V1, V2]; V1 by default. Send as value "v2". ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "redirect": "https://www.urlToRedirect.com", "key": "", "amount": "1.00", "currency": "USD", "orderNumber": "1212122", "capture": "1", "billToFirstName": "DEMO", "billToLastName": "DEMO", "billToAddress": "San Jose", "billToAddress2": "Catedral", "billToCity": "JS", "billToState": "SJ", "billToZipPostCode": "10061", "billToCountry": "CR", "billToTelephone": "88888888", "billToEmail": "cliente@ejemplo.com", "shipToFirstName": "DEMO", "shipToLastName": "DEMO", "shipToAddress": "San Jose", "shipToAddress2": "Catedral", "shipToCity": "JS", "shipToState": "SJ", "shipToZipPostCode": "10061", "shipToCountry": "CR", "shipToTelephone": "88888888", "subscription": "0", "platform": "api", "returnData": "dXNlcl9pZD0xMg==", "hashVersion": "V2", "token_version": "v2" } ``` ## Response [#respuesta] ```json { "type": "100", "html": "Use url redirect", "url": "https://secure.tilopay.com/htmls/1212122_574572148file.html" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Server-to-server > A service exclusive to PCI-certified merchants. The URL is provisioned per merchant: it is not self-serve. - kind: guide - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/server-to-server **Restricted access. This is not a self-serve path.** You cannot start this integration on your own today: it requires merchant PCI certification and a URL provisioned individually by Tilopay. ## What it is [#que-es] The path where card data passes through your server, which then sends it to the Tilopay API. ## Who it is for [#para-quien] - It is a **service exclusive to merchants holding PCI certification**. - The **URL is customized per merchant**, with its own key. **It is not a public URL.** That is why the examples on this page carry no real URL: the `Host` and the proxy key are delivered to each approved merchant. ## Requirements [#requisitos] 1. Valid PCI certification for your company. 2. An active Tilopay account. 3. An approved request, with the URL and key provisioned by Tilopay for your merchant. ## How to request it [#solicitud] Write to `soporte@tilopay.com` with your business name and the status of your PCI certification. The URL and key are delivered directly to the approved merchant. ## Request reference [#referencia] The `Host` and the `tx-proxy-key` header are delivered by Tilopay to each approved merchant. There is no public URL: the examples use placeholders. ```http POST / HTTP/1.1 Host: url provided by tilopay ``` ### Headers [#encabezados] `bearer {API token}` — see [authentication](/developers/api/autenticacion). Transaction proxy key, provided by Tilopay. ### Parameters [#parametros] Key tied to the merchant (required). Card number (required). Security code (required). Expiration date in monthYear format, for example `1023` (required). Cardholder first name (required). Cardholder last name (required). Customer phone (required). Customer email (required). Customer address (required). Customer city (required). State in ISO format, for example `CR-SJ` (San José, Costa Rica) or `US-CA` (California, USA) (required). Postal code (required). Country as ISO Alpha-2 code, for example `CR`, `US`, `GT` (required). Purchase amount (required). Currency in ISO format, for example `USD`, `CRC`, `GTQ` (required). Order number (required). `1` captures and authorizes, `0` only authorizes (required). Response URL (required). The `shipTo*` fields (`shipToFirstName`, `shipToLastName`, `shipToAddress`, `shipToAddress2`, `shipToCity`, `shipToState`, `shipToZipPostCode`, `shipToCountry`, `shipToTelephone`) appear in the collection examples as shipping data. ### Request [#peticion] ```json { "key": "key", "card": "card PAN", "cvv": "CVV Number", "expire": "1023", "name": "firstname", "lastname": "lastname", "phone": "88888888", "email": "email@user.com", "address": "user address", "city": "city", "state": "state", "zipcode": "zipcode", "country": "country", "shipToFirstName": "Nombre", "shipToLastName": "Apellido", "shipToAddress": "San Jose", "shipToAddress2": "Escazu", "shipToCity": "San Jose", "shipToState": "SJ", "shipToZipPostCode": "10101", "shipToCountry": "CR", "shipToTelephone": "88778877", "amount": "amount", "currency": "currency code", "orderNumber": "Order Number", "capture": 1, "redirect": "url to redirect" } ``` The collection includes two examples, **with 3DS** and **without 3DS**, sharing the same request body. ### Response [#respuesta] ```json { "code": "1", "description": "Transaccion aprobada", "auth": "123456", "orderNumber": "12345", "urlRedirect": "", "error": "" } ``` When the transaction requires 3DS authentication, the response carries the URL in `urlRedirect`; that is where the cardholder is sent. To interpret `code`, `description` and `error`, see [response conventions](/developers/api/convenciones) and [how to read an error response](/developers/api/procesos-operativos/errors). ## Alternatives without PCI certification [#alternativas] If you do not hold PCI certification, these paths avoid that scope entirely: - [Hosted payment page](/developers/hosted-payment-page) — Tilopay hosts the form. - [JavaScript SDK](/developers/sdk) — the form lives on your page, but the data travels from the browser straight to Tilopay. - [No code](/developers/sin-codigo) — a plugin or an already-integrated platform. --- # API · Payment links > Payment links operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/links-de-pago ## Operations [#operaciones] - [Create payment link](/developers/api/links-de-pago/create-link-payment) - [Payment link detail](/developers/api/links-de-pago/detail-link-payment) - [Delete a payment link](/developers/api/links-de-pago/delete-link-payment) - [List payment links](/developers/api/links-de-pago/payment-item-list) - [Get a payment link by id](/developers/api/links-de-pago/payment-by-id) Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · Create payment link > Payment link creation through the API, with response callback and webhook. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/links-de-pago/create-link-payment ```http POST /api/v1/createLinkPayment ``` ## What it does [#que-hace] Creates a payment link with amount, currency and usage behaviour. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Amount of the payment link. Currency code in ISO 4217 format. Reference of the payment link. Link type: 0 unlimited, 1 single use. Payment description. Customer name. It only applies when `type` is 1. Optional field. URL, over **GET**, the customer is redirected to with the response once the payment is completed. Example of the data sent: `code=Val&description=Val&auth=Val&tilopayLinkId=Val&orderNumber=Val&tilopayOrderId=Val&creditCardToken=Val&creditCardBrand=Val&last4CreditCardNumber=Val&orderHash=Val` Optional field. URL to send the response webhook to once the payment is completed. It must accept the **POST** method to receive the data. Example of the data sent: ```json { "code": "1", "codeDescription": "Aprobada", "auth": "123456", "tilopayLinkId": 54421, "orderNumber": "TPT123", "tilopayOrderId": 12233, "creditCardToken": "41111******11111", "creditCardBrand": "Visa", "last4CreditCardNumber": "1111", "linkDescription": "Descripcion del link de pago", "orderHash": "" } ``` ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "amount": "10", "currency": "USD", "reference": "123456", "type": 0, "description": "Description", "client": "Client name", "callback_url": "", "webhook_url": "" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "Success", "url": "https://app.tilopay.com/link/MjMwMjQ=", "id": 1 } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Payment link detail > Detail of a payment link and its payments, queried by id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/links-de-pago/detail-link-payment ```http GET /api/v1/getDetailLinkPayment/{link_payment_id}/{api_key} ``` ## What it does [#que-hace] Returns the detail of a payment link and its associated payments. It takes no body: the data travels in the path. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Path parameters, in the order they appear in the URL: Id of the payment link to query. Merchant api key. ## Response [#respuesta] ```json { "type": "200", "message": "Success", "detail": { "id": 6201, "amount": "45.00", "currency": "USD", "reference": "Producto de prueba 1 TP-Shop", "client": "NA", "type": "Unlimited", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "callback_url": null, "create": "2022-10-24 08:45:14" }, "payments": [] } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Delete a payment link > Deletion of a payment link by id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/links-de-pago/delete-link-payment ```http POST /api/v1/deleteLinkPayment ``` ## What it does [#que-hace] Deletes an existing payment link. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Payment link id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "35" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · List payment links > Paginated listing of the merchant's payment links. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/links-de-pago/payment-item-list ```http GET /api/v1/getLinkPaymentList/{api_key}/{limit} ``` ## What it does [#que-hace] Returns the paginated list of the merchant's payment links. It takes no body: the data travels in the path. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Path parameters, in the order they appear in the URL: Merchant api key. Number of records per page. ## Response [#respuesta] ```json { "type": "200", "message": "Success", "data": { "current_page": 1, "first_page_url": "http://app.tilopay.com/api/v1/getLinkPaymentList//10?page=1", "from": 1, "last_page": 2, "last_page_url": "http://app.tilopay.com/api/v1/getLinkPaymentList//10?page=2", "next_page_url": "http://app.tilopay.com/api/v1/getLinkPaymentList//10?page=2", "path": "http://app.tilopay.com/api/v1/getLinkPaymentList//10", "per_page": 10, "prev_page_url": null, "to": 10, "total": 16, "payment_link": [ { "id": 1, "amount": "45.00", "currency": "USD", "reference": "Producto de prueba 1 TP-Shop", "url": "https://admin.tilopay.com/link/3dNjE2MA3sdd==", "callback_url": null, "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/51087219_.png", "create": "2022-10-24 08:45:14" }, { "id": 2, "amount": "35.00", "currency": "USD", "reference": "Producto de prueba 2", "url": "https://admin.tilopay.com/link/dwNjE2see3MA==", "callback_url": null, "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/64156766_.png", "create": "2022-10-24 08:45:14" }, { "id": 3, "amount": "45.00", "currency": "USD", "reference": "Producto de prueba 3", "url": "https://admin.tilopay.com/link/f4NjE2MwsdA==", "callback_url": null, "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/88435879_.png", "create": "2022-10-24 08:45:14" }, { "id": 4, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/ceNjE2sxw2MA==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" }, { "id": 5, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/cweNjE2Mfd5A==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" }, { "id": 6, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/ce3NjE2fg5eMA==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" }, { "id": 7, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/dsNjE2MAd32==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" }, { "id": 8, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/NjE2sd32MA==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" }, { "id": 9, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/sd3NjE2MA==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" }, { "id": 10, "amount": "45.00", "currency": "USD", "reference": "test", "url": "https://admin.tilopay.com/link/d3dNjE2MA==", "callback_url": null, "description": "ddd", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/37716065_.png", "create": "2022-10-24 08:45:14" } ] } } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Get a payment link by id > Query of a single payment link by its id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/links-de-pago/payment-by-id ```http GET /api/v1/getLinkPaymentById/{link_payment_id}/{api_key} ``` ## What it does [#que-hace] Returns a single payment link. It takes no body: the data travels in the path. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Path parameters, in the order they appear in the URL: Id of the payment link to query. Merchant api key. ## Response [#respuesta] ```json { "type": "200", "message": "Success", "data": { "id": 1, "amount": "45.00", "currency": "USD", "reference": "Producto de prueba 1 TP-Shop", "url": "https://admin.tilopay.com/link/loNj8E2MA==", "callback_url": null, "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "times_used": null, "picture": "https://storage.googleapis.com/tilo-uploads/items/51087219_.png", "create": "2022-10-24 08:45:14", "payments": [] } } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Recurring charges > Recurring charges operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes ## Operations [#operaciones] 16 operations in three subgroups: - [Plans](/developers/api/recurrentes/planes) — 5 - [Subscribers](/developers/api/recurrentes/suscriptores) — 7 - [Coupons](/developers/api/recurrentes/cupones) — 4 Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · Recurring · Plans > Recurring · Plans operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/planes ## Operations [#operaciones] - [Create recurring plan](/developers/api/recurrentes/planes/create-plan) - [Edit recurring plan](/developers/api/recurrentes/planes/edit-plan) - [Get a recurring plan](/developers/api/recurrentes/planes/get-plan) - [List recurring plans](/developers/api/recurrentes/planes/get-plans) - [Delete recurring plan](/developers/api/recurrentes/planes/delete-plan) Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · Create recurring plan > Recurring plan creation: frequencies, free trial, modalities and webhooks. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/planes/create-plan ```http POST /api/v1/createPlanRepeat ``` ## What it does [#que-hace] Creates a subscription plan: charge frequency, currency, trial period, modalities and webhooks. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan title. Plan description. Charge frequency. | Code | Value | | --- | --- | | 1 | Daily | | 2 | Weekly | | 3 | Monthly | | 4 | Yearly | | 5 | Fortnightly | | 6 | Every two months | | 7 | Quarterly | | 8 | Every four months | | 9 | Every six months | Currency code in ISO 4217 format. Initial payment amount. Enables the free trial period: 0 no, 1 yes. Days of the free trial period. Number of retries for failed charges. Array of plan modalities. Optional field. The merchant's own thank-you URL; it must support the **GET** method. Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a customer successfully takes out a subscription. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'modality' : 'ModalityName', 'amount' : 25, 'frequency' : '', 'coupon' : '5HT5W8YT', 'free_trial' : 1, 'next_payment_date' : '2023-02-25'}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when the customer is charged successfully. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'amount' : 25, 'auth' : '123456', 'orderNumber' : 'PRE123456'}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a payment fails. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'amount' : 25}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a customer cancels the subscription to one of their plans. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'expire' : '2023-02-25'}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a customer reactivates the subscription to one of their plans. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'next_payment_date' : '2023-02-25'}` End date of the recurring plan, `d-m-Y` format (for example 25-09-2022). When the plan has no end date, send it empty. Set to 1 it adds the `notify_detail` and `notify_note` text to the notification email. Set to 0 it adds neither. Detail text in Spanish. Optional when `notify` is 0. Detail text in English. Optional when `notify` is 0. Notes text in Spanish. Optional when `notify` is 0. Notes text in English. Optional when `notify` is 0. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "title": "Plan title", "description": "Plan description", "frecuency": 1, "currency": "USD", "first_amount": 0, "trial": 0, "trial_days": 0, "attempts": 1, "modality": [ { "title": "Basic", "amount": 10 }, { "title": "Premium", "amount": 30 } ], "thanks_url": "", "webhook_subscribe": "", "webhook_payment": "", "webhook_rejected": "", "webhook_unsubscribe": "", "webhook_reactive": "", "end_at": "25-10-2023", "notify": 0, "notify_detail_es": "", "notify_detail_en": "", "notify_note_es": "", "notify_note_en": "" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success", "id": 624, "url": "https://app.tilopay.com/link/TmpJMHwx" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Edit recurring plan > Editing a recurring plan and its status. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/planes/edit-plan ```http POST /api/v1/editPlanRepeat ``` ## What it does [#que-hace] Edits an existing plan, including its status. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. Recurring plan title. Plan description. Charge frequency. | Code | Value | | --- | --- | | 1 | Daily | | 2 | Weekly | | 3 | Monthly | | 4 | Yearly | | 5 | Fortnightly | | 6 | Every two months | | 7 | Quarterly | | 8 | Every four months | | 9 | Every six months | Currency code in ISO 4217 format. Initial payment amount. Enables the free trial period: 0 no, 1 yes. Days of the free trial period. Number of retries for failed charges. Status of the recurring plan. | Code | Value | | --- | --- | | 0 | Inactive | | 1 | Active | | 2 | Active but with no new sign-ups | Optional field. The merchant's own thank-you URL; it must support the **GET** method. Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a customer successfully takes out a subscription. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'modality' : 'ModalityName', 'amount' : 25, 'frequency' : '', 'coupon' : '5HT5W8YT', 'free_trial' : 1, 'next_payment_date' : '2023-02-25'}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when the customer is charged successfully. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'amount' : 25, 'auth' : '123456', 'orderNumber' : 'PRE123456'}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a payment fails. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'amount' : 25}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a customer cancels the subscription to one of their plans. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'expire' : '2023-02-25'}` Optional field. Webhook URL, over **POST**, that receives the callback in the request body when a customer reactivates the subscription to one of their plans. Example of the data sent: `{'id_plan' : 1, 'email' : 'email@email.com', 'next_payment_date' : '2023-02-25'}` End date of the recurring plan, `d-m-Y` format (for example 25-09-2022). When the plan has no end date, send it empty. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": 3, "title": "Plan title", "description": "Plan description", "frecuency": 2, "currency": "USD", "first_amount": 0, "trial": 0, "trial_days": 0, "attempts": 5, "status": 1, "thanks_url": "", "webhook_subscribe": "", "webhook_payment": "", "webhook_rejected": "", "webhook_unsubscribe": "", "webhook_reactive": "", "end_at": "25-10-2023" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Get a recurring plan > Query of a recurring plan by id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/planes/get-plan ```http POST /api/v1/getPlanRepeat ``` ## What it does [#que-hace] Returns a single recurring plan. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "3" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "Success", "plan": { "id": 232, "title": "Prueba repeat", "description": "Plan repeat de prueba mensual", "frequency": 3, "currency": "USD", "first_amount": "5.00", "trial": 0, "trial_days": 0, "attempts": 5, "thanks_url": null, "webhook_subscribe": null, "webhook_payment": null, "webhook_rejected": null, "webhook_unsubscribe": null, "webhook_reactive": null, "modality": [ { "id": 292, "title": "Pago mensual prueba repeat modalidad", "amount": "10.00" } ], "end_at": null } } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · List recurring plans > Listing of the integration's recurring plans. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/planes/get-plans ```http POST /api/v1/getPlansRepeat ``` ## What it does [#que-hace] Returns the merchant's recurring plans. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Delete recurring plan > Deletion of a recurring plan by id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/planes/delete-plan ```http POST /api/v1/deletePlanRepeat ``` ## What it does [#que-hace] Deletes a recurring plan. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "625" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Recurring · Subscribers > Recurring · Subscribers operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores ## Operations [#operaciones] - [Subscribers of a plan](/developers/api/recurrentes/suscriptores/get-suscriptor-plan) - [Edit subscriber](/developers/api/recurrentes/suscriptores/edit-suscriptor-plan) - [Pause subscriber](/developers/api/recurrentes/suscriptores/pause-suscriptor-plan) - [Reactivate subscriber](/developers/api/recurrentes/suscriptores/reactive-suscriptor-plan) - [Delete subscriber](/developers/api/recurrentes/suscriptores/delete-suscriptor-plan) - [Registration and renewal URL](/developers/api/recurrentes/suscriptores/consult-url) - [Subscriber payments](/developers/api/recurrentes/suscriptores/suscriptor-payments) Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · Subscribers of a plan > Query of the subscribers of a recurring plan. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/get-suscriptor-plan ```http POST /api/v1/getSuscriptorRepeat ``` ## What it does [#que-hace] Returns the subscribers associated with a recurring plan. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "2" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "Success", "suscriptor": [ { "id": 360, "name": "Jhon 1", "lastname": "D", "email": "customer1@example.com", "modality": "Pago mensual prueba repeat modalidad", "amount": "10.00", "expire": "2023-03-26", "coupon": "", "status": "Delete", "create": "2022-10-19 12:48:59" }, { "id": 33364, "name": "Jhon 2", "lastname": "D", "email": "customer2@example.com", "modality": "Pago mensual prueba repeat modalidad", "amount": "10.00", "expire": "2023-08-05", "coupon": "", "status": "Active", "create": "2023-06-05 14:24:18" } ] } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Edit subscriber > Editing the status and expiration of a subscriber. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/edit-suscriptor-plan ```http POST /api/v1/editSuscriptorRepeat ``` ## What it does [#que-hace] Changes the status or the expiration date of a subscriber. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Subscriber id. Status of the subscriber. | Code | Value | | --- | --- | | 1 | Active | | 3 | Paused | | 4 | Deleted | Plan expiration date, `yy-m-d` format. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "2", "status": "2", "expire": "2023-10-25" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Pause subscriber > Pausing a subscriber's subscription. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/pause-suscriptor-plan ```http POST /api/v1/pauseSuscriptorRepeat ``` ## What it does [#que-hace] Pauses a subscriber's subscription. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Subscriber id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id_suscriptor": "1" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Reactivate subscriber > Reactivation of a subscriber's subscription. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/reactive-suscriptor-plan ```http POST /api/v1/reactiveSuscriptorRepeat ``` ## What it does [#que-hace] Reactivates a paused subscriber's subscription. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Subscriber id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id_suscriptor": "1" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Delete subscriber > Deletion of a subscriber from a recurring plan. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/delete-suscriptor-plan ```http POST /api/v1/deleteSuscriptorRepeat ``` ## What it does [#que-hace] Deletes a subscriber from a recurring plan. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Subscriber id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id_suscriptor": "1" } ``` ## Response [#respuesta] ```json { "type": "200", "status": 1, "message": "Success" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Registration and renewal URL > Registration or renewal URL of a recurring plan for a given email. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/consult-url ```http POST /api/v1/recurrentUrl ``` ## What it does [#que-hace] Returns the registration URL of a recurring plan for a new user, or the renewal URL when the email is already registered. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. Email of the user the registration link is created for. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "1", "email": "email@user.com" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "", "url_register": "https://app.tilopay.com/admin/recurrent/register/MjMy?email=email@user.com", "url_renew": "" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Subscriber payments > Payment history of the subscribers of a recurring plan. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/suscriptores/suscriptor-payments ```http POST /api/v1/getSuscriptorPayments ``` ## What it does [#que-hace] Returns the payments of the subscribers of a recurring plan. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "1" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Recurring · Coupons > Recurring · Coupons operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/cupones ## Operations [#operaciones] - [Create coupon](/developers/api/recurrentes/cupones/create-coupon) - [List coupons of a plan](/developers/api/recurrentes/cupones/get-coupons) - [Get a coupon](/developers/api/recurrentes/cupones/get-coupon) - [Delete coupon](/developers/api/recurrentes/cupones/delete-coupon) Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · Create coupon > Creation of discount coupons for recurring plans. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/cupones/create-coupon ```http POST /api/v1/createCoupon ``` ## What it does [#que-hace] Creates a discount coupon for a recurring plan: discount type, expiration, user and email permissions, and usage limits. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. User permission. | Code | Value | | --- | --- | | 0 | Allow new users only | | 1 | Allow already registered users | Discount type. | Code | Value | | --- | --- | | 1 | Percentage | | 2 | Fixed amount | Numeric value of the discount. Coupon expiration date, `Y-m-d` format (for example 2025-10-20). Email permission. | Code | Value | | --- | --- | | 1 | Specific emails | | 2 | Any email | Comma-separated list of emails. Required when `email_group` is sent as 1. Total number of uses. Total number of valid renewals. Total uses by the same user. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "planId": 10, "active_users": 1, "type_discount": 1, "discount": 40, "expire": "2025-10-20", "email_group": 1, "email": "cliente@ejemplo.com, cliente@ejemplo.com", "usage": 1, "renews": 2, "renews_by_user": 2 } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · List coupons of a plan > Listing of the coupons of a recurring plan. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/cupones/get-coupons ```http POST /api/v1/getRepeatCoupons ``` ## What it does [#que-hace] Returns the coupons associated with a recurring plan. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Recurring plan id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "1" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Get a coupon > Query of a recurring coupon by id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/cupones/get-coupon ```http POST /api/v1/getCoupon ``` ## What it does [#que-hace] Returns a single coupon. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Coupon id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "1" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Delete coupon > Deletion of a recurring coupon by id. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/recurrentes/cupones/delete-coupon ```http POST /api/v1/deleteCoupon ``` ## What it does [#que-hace] Deletes a coupon. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Coupon id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "id": "1" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Credential on File (CoF) > Credential on File (CoF) operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/cof ## Operations [#operaciones] 3 operations: - [Tokenize card](/developers/api/cof/process-tokenize) — 1 - [Remove card](/developers/api/cof/remove-card) — 1 - [Charge](/developers/api/cof/realizar-cobro) — 1 Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · processTokenize > Card tokenization with processTokenize: parameters, request and response. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/cof/process-tokenize ```http POST /api/v1/processTokenize ``` ## What it does [#que-hace] Tokenizes a card: returns the URL of the tokenization form. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Site where you wait for the transaction response. Key associated with the merchant. Cardholder email. Language [es, en]. Cardholder first name. Cardholder last name. Send as value "v2". ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "redirect": "https://urlToRedirect.com", "key": "", "email": "email@user.com", "language": "es", "firstName": "name", "lastName": "lastname", "token_version": "v2" } ``` ## Response [#respuesta] ```json { "type": "100", "url": "https://secure.tilopay.com/htmls/TOK-5063-740506121440_1648145457file.html" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Remove card > Removal of a saved card token from the merchant's private vault. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/cof/remove-card ```http POST /api/v1/user/card-remove ``` ## What it does [#que-hace] Removes a saved card token. **Requirement:** the merchant must have a private token vault. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Key associated with the merchant. Customer email associated with the token. Token to remove. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "1234-1234-1234-1234-1234", "email": "email@card.com", "token": "411111.......1111" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · CoF · Charge > Server-to-server charge with a v2 card token through Credential on File. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/cof/realizar-cobro ```http POST /api/v1/processRecurrentPayment ``` ## What it does [#que-hace] Processes a charge with an already tokenized card, without a form. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Key associated with the customer. It is obtained in Admin · Tilopay Checkout. Purchase amount. Purchase currency in ISO format, for example USD, CRC, GTQ. Order number, it can be alphanumeric. Capture and authorize: 1 yes, 0 no. Cardholder email. Card token. It must be the v2 token. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "amount": "10.00", "currency": "USD", "orderNumber": "1213", "capture": "1", "email": "myemail@exapmle.com", "card": "511111_00GOB1111" } ``` ## Response [#respuesta] ```json { "type": "200", "response": "1", "description": "Transacción aprobada", "auth": "123456", "tpt": 734329, "order_id": "12135", "tilopayTransaction": 734329, "orderHash": "f3ad85046d249bc7a781eed5285a5ded92045a2e11108063504deec72ec9b338" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Operational processes > Operational processes operations of the Tilopay API, with parameters and examples. - kind: api-index - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/procesos-operativos ## Operations [#operaciones] - [processModification](/developers/api/procesos-operativos/process-modification) - [consult](/developers/api/procesos-operativos/consult) - [consultTransactions](/developers/api/procesos-operativos/consult-transactions) - [Split settlement](/developers/api/procesos-operativos/split) These operations are cross-cutting: they work for any of the four [integration paths](/developers), no matter how the charge was originated. To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). Every operation lives on `https://app.tilopay.com` and, except for the login operations, requires the API token — see [authentication](/developers/api/autenticacion). --- # API · processModification > Capture, refund and reversal of a transaction with processModification. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/procesos-operativos/process-modification ```http POST /api/v1/processModification ``` ## What it does [#que-hace] Modifies an already processed transaction: capture, refund or reversal. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Example: `1214352` Example: `2` Example: `1.00` Key associated with the customer. It is obtained in Admin · Tilopay Checkout. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "orderNumber": "1214352", "type": "2", "amount": "1.00", "key": "" } ``` ## Response [#respuesta-modificacion] Type of modification applied. Transaction identifier. Result code of the modification. Result text. Order hash. See the note about `orderHash` in [webhooks](/developers/api/webhooks#verificacion). To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · consult > Query the status and processed amount of a single transaction. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/procesos-operativos/consult ```http POST /api/v1/consult ``` ## What it does [#que-hace] Queries a specific transaction: returns the processed amount and the status of the transaction. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Order number being queried. Merchant id. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "orderNumber": "1212122", "merchantId": "" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "", "response": [ { "id_tilopay": 734312, "orderNumber": "1212122", "amount": "5.00", "currency": "USD", "merchantId": "88803956", "code": "1", "response": "Transacción aprobada", "auth": "123456", "commission": 0.18, "iva_commission": 0.02, "retention_iva": 0.27, "retention_rent": 0.09, "cost": 0.35, "cost_iva": 0.05, "net_to_liquidate": "4.04", "capture": "Capture", "card": "4021", "last": "5221", "environment": "Test", "type": "Payment", "date": "2024-07-31 16:59:20" } ] } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · consultTransactions > Bulk transaction query by date range, currency, order or email. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/procesos-operativos/consult-transactions ```http POST /api/v1/consultTransactions ``` ## What it does [#que-hace] Queries transactions by date range, with optional filters. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Tilopay integration key. Start date, `Y-m-d H:i:s` format — for example "2022-01-15 00:00:00". End date, `Y-m-d H:i:s` format — for example "2022-08-01 23:59:59". Whether to return only approved transactions. | Code | Value | | --- | --- | | 0 | Any | | 1 | Approved only | Environment of the transactions to return. | Code | Value | | --- | --- | | 0 | Production | | 1 | Test | Array of currencies to return, for example ["USD", "CRC"]. Merchant id. Order number. Customer email. Authorization number. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "key": "", "startDate": "2023-06-01 00:00:00", "endDate": "2023-06-30 23:59:59", "onlyAproved": 1, "environment": 1, "currency": [ "USD", "CRC" ], "merchantId": "", "orderNumber": "12135", "email": "customer@example.com", "auth": "123456" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "", "response": [ { "id": 734329, "orderNumber": "12135", "amount": "5.00", "taxes": "0.00", "discount": "0.00", "discount_name": "", "currency": "USD", "merchantId": "", "code": "1", "response": "Transacción aprobada", "auth": "123456", "card": "4021", "last": "5221", "email": "customer@example.com", "commission": 0.18, "iva_commission": 0.02, "retention_iva": 0.27, "retention_rent": 0.09, "cost": 0.35, "cost_iva": 0.05, "net_to_liquidate": 4.04, "capture": "Capture", "type": "Payment", "environment": "Production", "date": "2024-07-31 16:59:20" }, { "id": 734330, "orderNumber": "12136", "amount": "5.00", "taxes": "0.00", "discount": "0.00", "discount_name": "", "currency": "USD", "merchantId": "", "code": "1", "response": "Transacción aprobada", "auth": "123456", "card": "4021", "last": "5221", "email": "customer@example.com", "commission": 0.18, "iva_commission": 0.02, "retention_iva": 0.27, "retention_rent": 0.09, "cost": 0.35, "cost_iva": 0.05, "net_to_liquidate": 4.04, "capture": "Capture", "type": "Payment", "environment": "Production", "date": "2024-07-31 17:25:35" } ] } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Split settlement > Splitting the settlement of an order among several facilitated merchants. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/procesos-operativos/split ```http POST /api/v1/orders/liquidation/split ``` ## What it does [#que-hace] Splits the settlement of an order among several merchants. It only applies to merchants that use Tilopay as a payment facilitator, and the resulting orders are identified by the `SL|` prefix. Rules: - The merchants must be approved and be from the same country. - If the split is lower than the total amount of the order, the remainder is assigned to the merchant that made the original transaction. - It only applies to fully approved orders. ## Authentication [#autenticacion] Requires the API bearer token in the `Authorization` header — see [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Id of the order approved in Tilopay. Associative array with the `email` and `amount` keys of every merchant the settlement is divided with. Including the owner merchant is optional: when its amount is not specified the remainder of the split is assigned to it, and when it is included and there is a remainder, the remainder is added to it. Request language. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "order_id": "1", "commerces": [ { "email": "commerce-1@example.com", "amount": "7.5" }, { "email": "commerce-2@example.com", "amount": "5.5" } ], "lang": "en" } ``` ## Response [#respuesta] ```json { "type": "200", "message": "Great", "description": "Order splitted successfully", "response": { "total_order_splitted": 3, "order_id": 1, "order_number": "PFC000069-TYP785237_313", "order_key": "", "order_currency": "USD", "order_amount": "33.00", "splitted_orders": [ { "id": 1, "amount": 20, "commerce_name": "Commerce owner name", "commerce_email": "owner@example.com" }, { "id": 2, "amount": 7.5, "commerce_name": "Commerce name", "commerce_email": "commerce-1@example.com" }, { "id": 3, "amount": 5.5, "commerce_name": "Commerce name", "commerce_email": "commerce-2@example.com" } ] } } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # API · Webhooks > Which events exist, how many retries there are, in what format they arrive and how to confirm the state of a charge. - kind: concept - status: stable - api_version: v1 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/api/webhooks ## Recurrence [#recurrencia] Six fields configured when **creating or editing a plan**. All optional. | Field | What it is | |---|---| | `webhook_subscribe` | Subscription webhook | | `webhook_payment` | Charge webhook | | `webhook_rejected` | Rejected charge webhook | | `webhook_unsubscribe` | Unsubscribe webhook | | `webhook_reactive` | Reactivation webhook | | `thanks_url` | **Not a webhook:** it is a GET callback | ## Payment links [#links-de-pago] `webhook_url` is configured when creating the link. ## Retries [#reintentos] | Source | Attempts | When it retries | |---|---|---| | Recurrence | 5 | When your response is 400, 404, 502, 504 or 403 | | Purchases | 1 | — | Delivery is considered successful on an **HTTP 200**. Respond 200 as soon as you receive the event and process it separately: anything else counts as a failure, and purchases have no second attempt. ## Format [#formato] JSON. ## The card token [#token] | Source | Token | |---|---| | Recurrence | **Not sent** | | Purchases | Sent and **usable** | ## Origin verification [#verificacion] The only origin verification available is the `orderHash`, and **its algorithm is not public**: it is delivered to merchants who request it by writing to `soporte@tilopay.com`. Until you have the algorithm, do not take irreversible business decisions from webhook content alone: confirm the state against the API before shipping or releasing a service. ## Main flow webhook [#processpayment] `processPayment` also has a webhook. Its technical specification (the fields it sends) **we deliver it when you request it**, so your team can implement it: write to `soporte@tilopay.com` to receive it. --- # JavaScript SDK > The integration path where the card form lives on your page and the SDK sends the data straight to Tilopay. - kind: guide - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk ## What it is [#que-es] The card fields live on your page. The V2 SDK takes over those inputs and sends the data **from the browser straight to Tilopay**: it never passes through your server. ## Who it is for [#para-quien] For teams that want to control the checkout design without card data touching their backend. You need a JavaScript frontend and a minimal backend to obtain the SDK token. ## How to build it, in order [#orden] 1. **Credentials.** There are no shared sandbox credentials: get yours through [developer registration](/developers/registro). 2. **SDK token.** Your backend requests it with `POST /api/v1/loginSdk`. See [authentication](/developers/api/autenticacion). 3. **Load the script.** See [installation](/developers/sdk/instalacion). 4. **Add the inputs with the contract ids** and the `responseTilopay` container. See [form fields](/developers/sdk/campos-del-formulario). 5. **Start the purchase** with [`Tilopay.Init()`](/developers/sdk/reference/init) and show the customer the payment methods it returns. 6. **Close the purchase** with [`Tilopay.startPayment()`](/developers/sdk/reference/startpayment). The SDK handles 3DS and renders the result on your `redirect` URL. ## Before assuming you are in test mode [#entornos] Testing and production share one host: the mode is verified with the `test` field returned by `Init()`. See [environments](/developers/entornos). ## Compliance [#cumplimiento] Card data does not pass through your server, but your page captures it: check the applicable scope with your compliance team. --- # API · Get SDK token > SDK login: returns the token that Tilopay.Init() uses in the browser. - kind: api-operation - status: stable - api_version: v1 - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/api/autenticacion/token-sdk ```http POST /api/v1/loginSdk ``` ## What it does [#que-hace] Returns the security token needed to consume the SDK. All three credentials come from [Admin · Tilopay Checkout](https://admin.tilopay.com/admin/checkout). ## Authentication [#autenticacion] It does not require a token: this is the operation that issues it. See [authentication](/developers/api/autenticacion). ## Parameters [#parametros] Merchant API user. Merchant API password. Merchant api key. ## Request example [#request] The `<...>` values are placeholders: replace them with your own credentials and data. ```json { "apiuser": "", "password": "", "key": "" } ``` ## Response [#respuesta] ```json { "access_token": "351d477adaaa51cdbb33ae4b4b8c843aa759f131c1c38415180b5035543d59d2f23f4e049706f5fa71c5f0d9ff763960a3cd035b4c1cefa428c90bedb672f780", "token_type": "bearer", "expires_in": "2023-06-05 13:32:06" } ``` To interpret a response that is not a success, see [how to read an error response](/developers/api/procesos-operativos/errors). --- # SDK installation > The V2 SDK script tag, which versions exist, and which versioning guarantees do not exist. - kind: guide - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/instalacion ## The script [#script] ```html ``` Load it before your own checkout code. The SDK exposes the global `Tilopay` object. ## jQuery is not required [#jquery] **jQuery is NOT required.** It was a leftover from SDK V1. The article published so far said otherwise and it is wrong: you can install SDK V2 without jQuery on the page. ## Versions [#versiones] There are only two versions: | Version | Status | |---|---| | `v1` | Unsupported | | `v2` | Current | ## What does not exist [#garantias] **There is no immutable versioning, no SRI hash and no changelog.** The `/sdk/v2/sdk_tpay.min.js` URL is not pinned to a specific version: its contents can change without prior notice and without public release notes. Practical consequences: - You cannot set `integrity="sha384-..."` on the `script` tag, because the file changes. - You cannot rely on a build number to reproduce a bug. - Keep smoke tests for your checkout running periodically, not only on deploy. ## What is next [#que-sigue] - [Form fields](/developers/sdk/campos-del-formulario) — the ids the SDK looks for. - [`Tilopay.Init()`](/developers/sdk/reference/init) — start the purchase. --- # Form fields > The tlpy_* ids the V2 SDK looks for in your form, and the responseTilopay container it needs for 3DS. - kind: concept - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/campos-del-formulario The V2 SDK does not render the form: it takes over the inputs that already exist on your page, finding them by `id`. Those ids are a contract — change one and the SDK stops finding the field. **The contract is the inputs, not the containers.** You can name the `div` elements that wrap the fields however you want: the SDK does not look for them. The only exception is `responseTilopay`, which is required. ## Fields [#campos] Holds the payment method id obtained from Tilopay. It may be visible or hidden, at your discretion. Holds the saved card id obtained from Tilopay. If the customer has no saved cards, hide it. Card number. Expiration date in month/year format, for example `01/25`. Security code. When the customer uses a saved card, this field must be enabled so they can type the CVV. Required container, **outside the form**. This is where the SDK mounts the 3DS flow. ## The Yappy phone does not travel through the DOM [#yappy] The Yappy phone number is **not** read from a form field: it is sent in the `phoneYappy` parameter of [`Init()`](/developers/sdk/reference/init) or [`updateOptions()`](/developers/sdk/reference/update-options). ## Example structure [#estructura] The `div` elements in this example are free-form; the input `id`s and `responseTilopay` are not. ```html
``` --- # SDK · Tilopay.Init() > Starts a purchase: authenticates the checkout and returns the available payment methods and the customer's saved cards. - kind: sdk-method - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/reference/init Starts a purchase. It authenticates the checkout with the SDK token and returns the payment methods available to you. To save a card without charging it, the SDK has a second start flow: **`Tilopay.InitTokenize()`**, with the same parameters as `Init()` except `amount`, `orderNumber`, `capture` and `subscription`, which do not apply because nothing is charged. ## Signature [#firma] ```js await Tilopay.Init({ /* parameters */ }) ``` ## Parameters [#parametros] SDK token, obtained with `POST /api/v1/loginSdk`. See [authentication](/developers/api/autenticacion). Purchase currency, ISO 4217. Checkout **language**, ISO 639-1. Only `es` and `en` are supported; defaults to `es`. Purchase amount. Customer email. Required for the response to include saved cards. Order number, unique per merchant. See [safe retries](/developers/reintentos-seguros). Customer identification type. Conditional. See the [type table](/developers/sdk/reference/update-options#tipos-de-identificacion). Customer identification number. Conditional. Customer first name. Customer last name. Customer address line 1. Customer address line 2. Optional. City. Recommended. State or province. Recommended. Postal code. Recommended. Country, ISO 3166-1 alpha-2. Recommended. Customer phone. Recommended. `0` authorizes; `1` authorizes and captures. URL where the SDK renders the final purchase response. `1` saves the customer card in Tilopay; `0` does not save it. Yappy phone. Required when paying with Yappy. It is not read from the DOM. Optional. Values `"V1"` or `"V2"`. If omitted, the final response hash is built with V1. Optional. Returned as-is in the final payment response and recovered on the transaction response URL. Supports up to **65,535 characters**, although a very long value can affect the response URL. You can send a base64-serialized array so it satisfies the string format. ## Call [#llamada] ```js const init = await Tilopay.Init({ token: sdkToken, currency: "CRC", language: "es", amount: 100.0, billToEmail: "cliente@ejemplo.com", orderNumber: "ORD-2026-000123", billToFirstName: "Ana", billToLastName: "Rojas", billToAddress: "Avenida 1, Local 2", billToCountry: "CR", capture: 1, redirect: "https://ejemplo.com/checkout/respuesta", subscription: 0, }); ``` ## Response [#respuesta] `Success`, or the error description. `0` production, `1` testing. See [environments](/developers/entornos). Object with `code` and `amount`, present when you have SINPE Móvil. The full data, including the destination phone, is obtained with [`getSinpeMovil()`](/developers/sdk/reference/get-sinpe-movil). Array of `{id, name, type}` with the available payment methods. Array of `{id, name, brand}` with the customer's saved cards. ## Payment method id format [#formato-id-metodo] Each method `id` has the shape `A:B:C`. **The second segment defines the payment method, and `18` corresponds to Yappy.** ## Saved cards [#tarjetas-guardadas] - For the response to include `cards`, **`billToEmail` is required**. - Once the cards are obtained, **the email can no longer be changed** with [`updateOptions()`](/developers/sdk/reference/update-options). --- # SDK · Tilopay.startPayment() > Closes the purchase. The SDK handles the whole 3DS flow and renders the response on the redirect URL. - kind: sdk-method - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/reference/startpayment Closes the purchase started with [`Tilopay.Init()`](/developers/sdk/reference/init). ## Signature [#firma] ```js await Tilopay.startPayment() ``` It takes no parameters. ## 3DS: the SDK handles it [#tres-ds] **The SDK handles the whole 3D Secure flow.** All you have to do is make sure the `responseTilopay` container is on the page: that is where the SDK inserts what 3DS needs and drives the process. ```html
``` See [form fields](/developers/sdk/campos-del-formulario#campos). ## Where the result arrives [#resultado] When it finishes, the SDK renders the response on the URL given in the `redirect` parameter of `Init()`. **That is the final purchase response**: treat the `redirect` URL as the point where your application decides the outcome. On error, the method returns the description: ```json { "message": "error description" } ``` --- # Tilopay.getCardType() > Returns the brand of the card the customer is typing, so you can show an icon while they type. - kind: sdk-method - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/reference/get-card-type Returns the card brand from the number the customer already typed into `tlpy_cc_number`. The typical use is showing the brand icon while the customer types. ## Signature [#firma] ```js await Tilopay.getCardType() ``` It takes no parameters. **Precondition:** the customer must have already typed the card number. ## Call [#llamada] ```js const type = await Tilopay.getCardType(); ``` ## Response [#respuesta] ```json { "message": "visa" } ``` The card brand. ## Supported brands [#marcas-soportadas] VISA · MASTERCARD · AMEX Use the result for presentation only: brand validation happens when Tilopay processes the charge, not in this method. --- # Tilopay.getSinpeMovil() > Returns the data to show the customer to pay via SINPE Móvil and activates the collection listener. - kind: sdk-method - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/reference/get-sinpe-movil SINPE Móvil is not charged from the form: the customer makes the transfer from their own bank. This method returns the data you must show them — destination phone, exact amount and description code. ## When to use it [#cuando] When you have a SINPE Móvil method available and the customer selects it from the list returned by [`Init()`](/developers/sdk/reference/init). **It is not just a getter.** Besides returning the data, this method **triggers the SINPE listener with the Tilopay backend**. Calling it is part of the collection flow, not a presentation detail. ## Signature [#firma] ```js await Tilopay.getSinpeMovil() ``` It takes no parameters. **Preconditions:** - The customer must have selected SINPE Móvil as the payment method. - `typeDni` and `dni` must have been sent earlier, via `Init()` or [`updateOptions()`](/developers/sdk/reference/update-options). ## Call [#llamada] ```js const params = await Tilopay.getSinpeMovil(); ``` ## Response [#respuesta] ```json { "message": "Success", "code": "863", "amount": 10, "number": "70599200" } ``` `Success` when the operation succeeded. The code the customer must write in the transfer description. It is what identifies the payment. The exact amount they must transfer. The destination phone number for the transfer. ### On error [#respuesta-de-error] **On error it returns nothing.** Do not expect an object with empty fields: treat the absence of a response as the error case. ## What to do with this data [#que-hacer] When SINPE Móvil is selected, hide the card fields and show the customer this data: destination phone, exact amount and the code for the description. **No pay button is needed:** the collection is processed when the transfer arrives. --- # Tilopay.updateOptions() > Partially updates the parameters sent to Init() when they change before closing the payment. - kind: sdk-method - status: stable - sdk_version: v2 - last_verified: 2026-08-28 - url: https://tilopay.com/en/developers/sdk/reference/update-options Updates values already sent in [`Tilopay.Init()`](/developers/sdk/reference/init) that changed during checkout — typically when the customer edits their data, or when they select SINPE Móvil or Yappy and information that did not apply before must be added. **It is a patch.** Send only the fields you want to update; there is no need to resend everything. ## What "required" means here [#obligatorio] In the list below, "required" means the field **must have been sent either in `Init()` or in `updateOptions()`**. If it was never sent in either, the payment cannot proceed. It does not mean you must resend it on every call. ## Signature [#firma] ```js await Tilopay.updateOptions({ /* only the fields that change */ }) ``` ## Call [#llamada] ```js const update = await Tilopay.updateOptions({ typeDni: 1, dni: "0707770777", billToFirstName: "Ana", billToLastName: "Rojas", }); ``` ## Parameters [#parametros] Customer identification type. Required for SINPE Móvil. See [identification types](#tipos-de-identificacion). Customer identification number. Required for SINPE Móvil. **Accepted with and without hyphens.** Customer first name. Customer last name. Customer address line 1. Customer address line 2. Optional. Customer city. Recommended. State or province. Recommended. Postal code. Recommended. Country, ISO 3166-1 alpha-2. Recommended. Customer phone. Recommended. `0` authorizes; `1` authorizes and captures. URL where the final purchase response is expected. `1` if the customer wants to save their card in Tilopay; `0` if not. Yappy phone. Required when the customer pays with Yappy. **`billToEmail` cannot be updated after saved cards are obtained.** Once `Init()` returned the customer's cards, the email cannot be changed through this method. ## Identification types [#tipos-de-identificacion] Applies to the `typeDni` parameter, both here and in `Tilopay.Init()`. In the masks, `#` is a **numeric** character and `&` is **alphanumeric**. Lengths do not count hyphens. | Code | Type | Format | Length | |---|---|---|---| | 1 | Cédula de identidad | `0#-####-####` | 10 | | 2 | Cédula jurídica | `3-###-######` | 10 | | 3 | Gobierno central | `2-###-######` | 10 | | 4 | Institución autónoma | `4-###-######` | 10 | | 5 | Extranjero no residente | `9&&&&&&&&&&&&&&&&&&&` | 20 | | 6 | DIMEX | `1###########` | 12 | | 7 | DIDI | `5###########` | 12 | ## Response [#respuesta] ```json { "message": "Success" } ``` `Success` when the update succeeded; the error description otherwise. --- # Tilopay Banking API > Reference for Tilopay's Banking API (BaaS), generated from its OpenAPI 3.0.3 spec, version 1.0.0. - kind: api-index - status: stable - api_version: 1.0.0 - last_verified: 2026-10-09 - url: https://tilopay.com/en/developers/api-bancario With our Banking API you can initiate payments (PIN and SINPE Móvil), check their status, manage your accounts and receive payment event webhooks from your own systems, always limited to the accounts your access token authorizes. **It is a separate product from the payment gateway.** It runs on its own infrastructure, with its own host, credentials and authentication. If you already integrate our acquiring API, access to this one is enabled separately. Here you will find the official endpoints, models and flows for version **1.0.0** of the Banking API. ## What it can do [#que-permite] **Payments.** Create a payment, fetch it by identifier, list it by account and search it by `payment_id`, `public_id` or `client_reference`; the available methods are `PIN` and `SINPE_MOVIL`, in `OUT` or `IN` direction. [See payment operations](/developers/api-bancario/pagos). **Accounts.** Validate a destination account before transferring, list your accounts with or without balances, check an account balance and request statements asynchronously. [See account operations](/developers/api-bancario/cuentas). **Webhooks.** Register your encrypted HTTPS destination, see the events you can subscribe to per payment method and the data structure we send in each one. [See webhook operations](/developers/api-bancario/webhooks). **Catalogs.** Customer identification person types per country. [See catalogs](/developers/api-bancario/catalogos). We also provide `GET /api/public/v1/healthz` for health checks, plus `GET /openapi.yaml` and `GET /docs` with the OpenAPI definition and interactive documentation right on the API host. ## Environments [#entornos] There are two environments, with the same routes and contracts. The sandbox one: ```text https://api-baas-sandbox.tilopay.com ``` Production is the same host without the `-sandbox` suffix: ```text https://api-baas.tilopay.com ``` You can start in sandbox and, when you are ready, we hand over your production credentials as we enable your access. ## Machine-readable artifacts [#artefactos] We publish the full Banking API OpenAPI definition, with open CORS, in two formats: - [openapi.json](/developers/api-bancario/openapi.json) - [openapi.yaml](/developers/api-bancario/openapi.yaml) It is independent from our acquiring API definition ([/developers/openapi.json](/developers/openapi.json)): they are two separate products. ## How to get access [#acceso] Access is restricted and granted on request, for operations in Costa Rica. To learn about the product and request it, visit [baas.tilopay.com](https://baas.tilopay.com), stating estimated monthly volume, use case and systems to integrate. ## Where to go next [#siguiente] 1. [Authentication](/developers/api-bancario/autenticacion) — login, token exchange and Bearer. 2. [Conventions](/developers/api-bancario/convenciones) — envelope, correlation, safe retries, pagination and dates. 3. [Response codes](/developers/api-bancario/errores) — full `response_code` catalog. --- # Banking API authentication > Login, token exchange and Bearer JWT usage in Tilopay's Banking API. - kind: concept - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/autenticacion The Banking API uses a two-step flow: first you authenticate the customer with email and password, then you exchange the resulting token for a JWT access token that signs every other call. The three endpoints in the `Auth` group are the only ones that carry **no** `Authorization` header. Every other endpoint requires `Authorization: Bearer `. ## Step 1 — Login [#login] ```http POST /api/public/v1/auth/login ``` Required body: `email` (email format) and `password`. ```json { "email": "operations@merchant.com", "password": "••••••••" } ``` The `200` response returns in `data`: | Field | Type | Description | |---|---|---| | `gidp_id_token` | string | Identity token for step 2. | | `assignments` | array | Available customer contexts. | Each `assignments` item carries `assignment_id`, `owner_type`, `owner_id`, `tenant_id`, `tenant_code`, `country_code`, `role`, `roles` and `status`. Pick the `assignment_id` of the context you will operate with. ## Step 2 — Token exchange [#exchange] ```http POST /api/public/v1/auth/token/exchange ``` Required body: `gidp_id_token` (from step 1) and `assignment_id` (the chosen context). ```json { "gidp_id_token": "eyJhbGciOi...", "assignment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6" } ``` The `200` response returns in `data`: | Field | Type | Description | |---|---|---| | `token` | string | JWT access token. | | `expires_in` | integer | Token lifetime in seconds. | | `claims` | object | Resolved token context. | `claims` includes `sub`, `subject_kind`, `user_type`, `tenant_id`, `owner_type`, `owner_id`, `roles`, `scopes`, `country_code`, `locale`, `channel`, `idp`, `idp_sub` and `idempotency_key`. ## Step 3 — Use the Bearer token [#bearer] The declared security scheme is `BearerAuth`: HTTP bearer with JWT format. ```http Authorization: Bearer ``` Context travels **inside** the token: business operations do not take `tenant_id`, `owner_id` or `owner_type` as parameters. The token alone scopes which accounts and payments you can reach. When the token is absent, expired or invalid, the response is `401` with `response_code: UNAUTHORIZED`. If the token is valid but the context does not authorize the resource, it is `403` with `FORBIDDEN`. Repeat step 2 to renew the access token before `expires_in` elapses. ## Passwords [#password] ```http POST /api/public/v1/auth/request-set-password ``` Starts the set/reset password process for the given email. It requires no prior authentication and always responds the same way whether or not the account exists, so it never reveals if an email is registered. ```json { "email": "operations@merchant.com" } ``` --- # Banking API conventions > Response envelope, correlation, safe retries, pagination and date format in the Banking API. - kind: concept - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/convenciones Every Banking API operation shares the same response shape, the same pagination rules and the same date format. Here we explain those shared rules. ## Response envelope [#envelope] Every response, successful or not, travels in the same envelope: ```json { "success": true, "http_status_code": 200, "response_code": "OK", "message": "The request was processed successfully.", "correlation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "data": {} } ``` | Field | Type | Description | |---|---|---| | `success` | boolean | `true` on successful responses; `false` on errors. | | `http_status_code` | integer | Repeats the HTTP status inside the body. | | `response_code` | string | Code from the [`response_code` catalog](/developers/api-bancario/errores). | | `message` | string | Human-readable response message. | | `correlation_id` | string | Call identifier. | | `data` | object | Operation payload. | Error responses also carry `errors`, a detail object that may include arbitrary fields such as `code`, `message` or `detail`. Branch your logic on `response_code`, never on the `message` text. ## Correlation [#correlacion] Send `X-Correlation-Id` on the request to propagate your own identifier; the value comes back as `correlation_id` in the envelope. Keep it in your logs: it is what support uses to trace a specific call. ## Safe retries [#reintentos] Two operations accept the `Idempotency-Key` header: ```http POST /api/public/v1/transactions/payments POST /api/public/v1/accounts/statements ``` Resending the same key with the same body returns the original result instead of creating a second record, so a network timeout can be retried without duplicating. If you reuse a key with a different body, the response is `409` with `response_code: IDEMPOTENCY_CONFLICT`. Generate one key per business intent (for example a UUID per order), not one per retry. Every other operation is a read and can be repeated with no additional effects. ## Pagination [#paginacion] The API uses two schemes depending on the resource. **Cursor, on the payment list.** `GET /api/public/v1/transactions/payments` accepts `limit` (default 20, maximum 100) and `cursor`. The response carries `pagination.next_cursor`: pass it as `cursor` on the next request. When it is empty or omitted, there are no more pages. **Offset, on accounts.** `GET /api/public/v1/accounts` and `GET /api/public/v1/accounts/balances` accept `limit` (1 to 100) and `offset` (from 0), and return `pagination` with `limit`, `offset` and `total`. ## Dates and amounts [#formatos] Date parameters and fields use RFC 3339, for example `2026-09-02T15:04:05Z`. The `date_from` and `date_to` filters on the payment list follow that same format. ## Host health and documentation [#host] The host itself exposes three service routes: ```http GET /api/public/v1/healthz GET /openapi.yaml GET /docs ``` `healthz` is for availability checks, `openapi.yaml` returns the OpenAPI definition and `/docs` the interactive documentation. We also publish it in this portal at [openapi.json](/developers/api-bancario/openapi.json) and [openapi.yaml](/developers/api-bancario/openapi.yaml). --- # Banking API payments > Banking API payment operations: create, fetch, list and search PIN and SINPE Móvil payments. - kind: api-operation - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/pagos The `Payments` group covers a payment's full cycle: create it, fetch it by identifier, list it with filters and search it by any of its references. ```http POST /api/public/v1/transactions/payments GET /api/public/v1/transactions/payments GET /api/public/v1/transactions/payments/{payment_id} POST /api/public/v1/transactions/payments/search ``` ## Resource rules [#reglas] **Methods.** `payment_method_code` accepts `PIN` and `SINPE_MOVIL`. **Direction.** `direction` accepts `OUT` (outbound) and `IN` (inbound). **Destination account.** it is identified by `IBAN` or by `PHONE`, depending on the method. **Your own reference.** `client_reference` is your payment identifier and is unique per tenant: two payments of the same customer cannot share it. Use it to look a payment up without storing the `payment_id`. **Safe retries.** Creation accepts `Idempotency-Key`; resending the same key with the same body returns the original payment instead of creating another one. See [safe retries](/developers/api-bancario/convenciones#reintentos). ## States [#estados] A payment moves through five states: `pending`, `processing`, `confirmed`, `posted` and `failed`. A reversal is **not a state**: a reversed payment stays in `posted` and is recognized by `result: reversed` and `has_reversal: true`, plus the [`payment.reversed`](/developers/api-bancario/webhooks) event. Always check those two fields before treating a payment as final. ## Operations [#operaciones] ### List customer payments [#get-transactions-payments] ```http GET /api/public/v1/transactions/payments ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Simple REST list of payments for an owned account (IBAN). Filters are query parameters. Ownership (`tenant_id`, `owner_type`, `owner_id`) is derived from the access token; clients must not send ownership filters. **Account identification (required):** `type` + `value` (identifier scheme + value, e.g. `IBAN` + IBAN string). This is not the internal account classification. Pagination is cursor-based (`limit`, `cursor`). Default limit is 20 (max 100). `status` in the response and as a filter uses the public lowercase catalog (`pending`, `processing`, `confirmed`, `posted`, `failed`). Same values as webhooks. REST also returns `status_detail` (uppercase operational name). Each item includes a consolidated `result` (`succeeded`, `failed`, `reversed`, `pending`) for reconciliation when a webhook was missed. `status` is only the processing/lifecycle catalog and does not change to `failed` when a posted payment is later reversed — use `result=reversed` and `has_reversal=true` (webhook `payment.reversed`). `error` is `null` when there is no failure information; it is an object only when `domain` / `platform` is present (`result` is `failed` or `reversed`), same shape as webhook `payment.failed` `payment.error`. `source` / `destination` use `null` for unknown `account`, `name`, and `identification`; account values are IBAN or PHONE only (never ledger UUIDs). For PIN payments created after public identification persistence, `identification.number` is the partner-submitted public number (no SINPE hyphens); older payments may still show the hyphenated SINPE form. See `docs/PAYMENT-IDENTIFICATION.md`. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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. | | `type` | query | string | yes | Account **identifier scheme** (e.g. `IBAN`). This is **not** the internal account classification (e.g. operational). Values: IBAN | | `value` | query | string | yes | Account identifier value (IBAN). Spaces are ignored. | | `status` | query | string | — | Optional payment status filter. Public catalog is lowercase: `pending`, `processing`, `confirmed`, `posted`, `failed`. Uppercase aliases of the same values are accepted. Legacy values such as `INITIATED` map to `pending`. Values: pending, processing, confirmed, posted, failed | | `date_from` | query | string (date-time) | — | Inclusive lower bound for `created_at`. RFC 3339 (UTC `Z` or offset; optional fractional seconds). | | `date_to` | query | string (date-time) | — | Inclusive upper bound for `created_at`. RFC 3339 (UTC `Z` or offset; optional fractional seconds). | | `client_reference` | query | string | — | Filter by partner client reference (exact match against the value sent at payment creation). | | `currency` | query | string | — | Filter by payment currency (ISO 4217, e.g. CRC). | | `payment_method_code` | query | string | — | Filter by public payment method code. Values: PIN, SINPE_MOVIL | | `direction` | query | string | — | Filter by payment direction. Accepts public values `OUT`/`IN` or `PAYOUT`/`PAYIN` (internally normalized to `payout`/`payin`). Values: OUT, IN, PAYOUT, PAYIN | | `limit` | query | integer | — | Page size (default 20, max 100). | | `cursor` | query | string | — | Opaque cursor from a previous response `pagination.next_cursor`. | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | — | | `items[].amount` | object | yes | — | | `items[].amount.amount` | string | yes | — | | `items[].amount.currency` | string | yes | — | | `items[].client_reference` | string | yes | — | | `items[].created_at` | string | yes | — | | `items[].destination` | object | yes | — | | `items[].destination.account` | object | yes | — | | `items[].destination.identification` | object | yes | — | | `items[].destination.name` | string | yes | — | | `items[].error` | object | yes | — | | `items[].error.domain` | string | yes | — | | `items[].error.platform` | object | yes | — | | `items[].external_reference` | string | yes | — | | `items[].has_reversal` | boolean | yes | — | | `items[].payment_id` | string | yes | — | | `items[].payment_method_code` | string | yes | — | | `items[].provider` | object | yes | — | | `items[].provider.correlation_id` | string | yes | — | | `items[].provider.occurred_at` | string | yes | — | | `items[].provider.provider_status_code` | string | yes | — | | `items[].provider.provider_status_desc` | string | yes | — | | `items[].provider.provider_status_semantic` | string | yes | — | | `items[].public_id` | string | yes | — | | `items[].result` | string | yes | — | | `items[].reversed_at` | string | yes | — | | `items[].schema_version` | string | yes | — | | `items[].source` | object | yes | — | | `items[].source.account` | object | yes | — | | `items[].source.identification` | object | yes | — | | `items[].source.name` | string | yes | — | | `items[].status` | string | yes | — | | `items[].status_detail` | string | — | — | | `items[].succeeded_at` | string | yes | — | | `items[].type` | string | yes | — | | `items[].updated_at` | string | yes | — | | `pagination` | object | — | — | | `pagination.limit` | integer | yes | — | | `pagination.next_cursor` | string | — | — | | `schema_version` | string | yes | — | **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`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 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. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Create payment [#post-transactions-payments] ```http POST /api/public/v1/transactions/payments ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Creates a payment using an authorized internal source account. Processing may be asynchronous; HTTP 201 does not imply the payment is finalized. `source.identification.number` and `destination.identification.number` are stored in public form (the submitted digits, without SINPE hyphens) and returned on later payment reads. The GX/SINPE hyphenated form is used only internally and is not the public contract. Idempotency-Key is optional; if omitted, each request is treated as a new attempt. A repeated `client_reference` returns 409 PAYMENT_DUPLICATE when an in-flight or completed payment already uses it; a prior `failed` (or rejected/cancelled/expired) payment does not block a retry with the same `client_reference`. If Idempotency-Key is provided, the same key with the same payload replays the prior response; a different payload with the same key returns 409 IDEMPOTENCY_CONFLICT. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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 | — | Optional opaque client key for safe retries. When omitted, each call is a new attempt (duplicates are detected by client_reference on in-flight or completed payments). Reusing a key with a different body returns 409. | **Request body** | Field | Type | Required | Description | |---|---|---|---| | `payment_method_code` | string | yes | Public payment method catalog (`PIN`, `SINPE_MOVIL`). Values: PIN, SINPE_MOVIL | | `direction` | string | yes | Operation direction (OUT = payout from internal source) Values: OUT, IN | | `country_code` | string | — | Operational country when multi-country applies | | `amount` | object | yes | — | | `amount.amount` | string | — | — | | `amount.currency` | string | — | — | | `source` | object | yes | — | | `source.account` | object | yes | — | | `source.account.type` | string | yes | Account **identifier scheme**. Only `IBAN` is supported here. This is not the internal account classification (e.g. operational). Values: IBAN | | `source.account.value` | string | yes | IBAN (spaces allowed; normalized server-side) | | `source.name` | string | yes | Account holder name | | `source.identification` | object | yes | Counterparty identification for payment create. `type` uses platform identification-catalog keys (e.g. `cedula`, `juridica`, `dimex`). Same vocabulary as `holder.id_type` from `POST /accounts/validate`. `number` is the public identification (digits, no SINPE hyphens). The platform may reformat it internally for GX/SINPE; that internal form is not echoed on public payment reads for payments created after public identification persistence. For SINPE_MOVIL, account validate may return `unknown` because the wallet operation does not expose identification type; do not assume a confirmed type. | | `source.identification.type` | string | — | Identification catalog key (lowercase), e.g. `cedula`, `juridica`. May be copied from `holder.id_type` after account validate. For SINPE_MOVIL that value may be `unknown` (expected limitation, not an error). | | `source.identification.number` | string | — | Public identification number as submitted by the partner. Digits only; do not send SINPE hyphenation (e.g. `01-1234-3005`) or treat this field as the internal GX/SINPE display form. Supported types include `cedula`, `juridica`, `dimex`, `gobierno`, `institucion_autonoma`, `didi`, and `pasaporte`. | | `destination` | object | yes | — | | `destination.account` | object | yes | — | | `destination.account.type` | string | yes | Counterparty account **identifier scheme** (`IBAN` for PIN, `PHONE` for SINPE_MOVIL). Not the internal account classification. Values: IBAN, PHONE | | `destination.account.value` | string | yes | — | | `destination.name` | string | yes | Counterparty name | | `destination.identification` | object | yes | Counterparty identification for payment create. `type` uses platform identification-catalog keys (e.g. `cedula`, `juridica`, `dimex`). Same vocabulary as `holder.id_type` from `POST /accounts/validate`. `number` is the public identification (digits, no SINPE hyphens). The platform may reformat it internally for GX/SINPE; that internal form is not echoed on public payment reads for payments created after public identification persistence. For SINPE_MOVIL, account validate may return `unknown` because the wallet operation does not expose identification type; do not assume a confirmed type. | | `destination.identification.type` | string | — | Identification catalog key (lowercase), e.g. `cedula`, `juridica`. May be copied from `holder.id_type` after account validate. For SINPE_MOVIL that value may be `unknown` (expected limitation, not an error). | | `destination.identification.number` | string | — | Public identification number as submitted by the partner. Digits only; do not send SINPE hyphenation (e.g. `01-1234-3005`) or treat this field as the internal GX/SINPE display form. Supported types include `cedula`, `juridica`, `dimex`, `gobierno`, `institucion_autonoma`, `didi`, and `pasaporte`. | | `detail_reference` | string | yes | Operational / transactional description stored on the payment | | `client_reference` | string | — | Partner functional reference. Unique per tenant among in-flight and completed payments; a failed attempt can be retried with the same value. | | `channel_reference` | string | — | — | | `originator` | object | — | Optional Ultimate Debtor / Travel Rule originator information. Informational only in this release; no validation is applied. Stored under payment metadata when provided. | | `originator.company` | object | — | — | | `originator.company.legal_name` | string | — | — | | `originator.company.id_type` | string | — | — | | `originator.company.id_number` | string | — | — | | `originator.customer` | object | — | — | | `originator.customer.name` | string | — | — | | `originator.customer.id_type` | string | — | — | | `originator.customer.id_number` | string | — | — | **201** — The resource was created successfully. `response_code`: `CREATED` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `created_at` | string | yes | — | | `payment_id` | string | yes | — | | `payment_method_code` | string | yes | — | | `public_id` | string | yes | — | | `status` | string | yes | — | | `status_detail` | string | yes | — | **Error responses** | HTTP | response_code | Description | |---|---|---| | 400 | `PAYMENT_INVALID_PAYLOAD`, `INVALID_PAYMENT_METHOD`, `INVALID_ACCOUNT_TYPE`, `ACCOUNT_INVALID`, `ACCOUNT_HOLDER_MISMATCH` | Invalid payment request payload. | | 401 | `UNAUTHORIZED` | Unauthorized. Verify your session or credentials. | | 403 | `FORBIDDEN`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 404 | `ACCOUNT_NOT_FOUND` | Account not found. | | 409 | `IDEMPOTENCY_CONFLICT`, `PAYMENT_DUPLICATE`, `CONFLICT` | Idempotency conflict: the same Idempotency-Key was reused with a different request body, or a previous request is still in progress. | | 422 | `INSUFFICIENT_FUNDS`, `LIMIT_EXCEEDED`, `UNPROCESSABLE_ENTITY` | Insufficient funds to complete the payment. | | 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. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Search payment by identifier [#post-transactions-payments-search] ```http POST /api/public/v1/transactions/payments/search ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Looks up a single payment within the supplied customer account by exactly one of: - `payment_id` (internal UUID) - `public_id` (numeric public identifier) - `client_reference` (partner reference sent at creation) Sending more than one identifier returns `400`. Response shape matches get-by-id (`schema_version`, payment-level `external_reference`, shared `provider` object, `provider.provider_status_*`, webhook-aligned `error`). Same public contract as `GET /transactions/payments`, including public `identification.number` for PIN payments created after public identification persistence. If several payments share a `client_reference` (failed attempt plus retry), the in-flight or completed payment is returned when one exists. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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** (e.g. `IBAN`, `PHONE`). Not the internal account classification (e.g. operational). Values: IBAN, PHONE | | `account.value` | string | yes | — | | `account.currency` | string | — | Optional account currency in response payloads. | | `payment_id` | string (uuid) | — | Internal payment UUID. | | `public_id` | string | — | Numeric public payment identifier. | | `client_reference` | string | — | Partner reference sent at payment creation. | **Request example** — byPaymentId ```json { "account": { "type": "IBAN", "value": "CR08036900101010933608" }, "payment_id": "11111111-1111-1111-1111-111111111111" } ``` **Request example** — byPublicId ```json { "account": { "type": "IBAN", "value": "CR08036900101010933608" }, "public_id": "134729" } ``` **Request example** — byClientReference ```json { "account": { "type": "IBAN", "value": "CR08036900101010933608" }, "client_reference": "INV-01082" } ``` **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `amount` | object | yes | — | | `amount.amount` | string | yes | — | | `amount.currency` | string | yes | — | | `client_reference` | string | yes | — | | `created_at` | string | yes | — | | `destination` | object | yes | — | | `destination.account` | object | yes | — | | `destination.account.currency` | string | — | — | | `destination.account.type` | string | yes | — | | `destination.account.value` | string | yes | — | | `destination.identification` | object | yes | — | | `destination.identification.number` | string | yes | — | | `destination.identification.type` | string | yes | — | | `destination.name` | string | yes | — | | `error` | object | yes | — | | `error.domain` | string | yes | — | | `error.platform` | object | yes | — | | `error.platform.code` | string | yes | — | | `error.platform.message` | string | yes | — | | `external_reference` | string | yes | — | | `has_reversal` | boolean | yes | — | | `payment_id` | string | yes | — | | `payment_method_code` | string | yes | — | | `provider` | object | yes | — | | `provider.correlation_id` | string | yes | — | | `provider.occurred_at` | string | yes | — | | `provider.provider_status_code` | string | yes | — | | `provider.provider_status_desc` | string | yes | — | | `provider.provider_status_semantic` | string | yes | — | | `public_id` | string | yes | — | | `result` | string | yes | — | | `reversed_at` | string | yes | — | | `schema_version` | string | yes | — | | `source` | object | yes | — | | `source.account` | object | yes | — | | `source.account.currency` | string | — | — | | `source.account.type` | string | yes | — | | `source.account.value` | string | yes | — | | `source.identification` | object | yes | — | | `source.identification.number` | string | yes | — | | `source.identification.type` | string | yes | — | | `source.name` | string | yes | — | | `status` | string | yes | — | | `status_detail` | string | — | — | | `succeeded_at` | string | yes | — | | `type` | string | yes | — | | `updated_at` | string | yes | — | **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`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 404 | `PAYMENT_NOT_FOUND` | Payment not found. | | 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. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Get payment by id [#get-transactions-payments-payment-id] ```http GET /api/public/v1/transactions/payments/{payment_id} ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Returns a single payment using the same public contract as the list endpoint, including consolidated `result`, `succeeded_at`, `has_reversal`, and `error`. `error` is `null` when there is no failure information; it is an object only when `domain` / `platform` is present (`result` is `failed` or `reversed`), same shape as webhook `payment.failed` `payment.error`. `source` / `destination` use `null` for unknown nested fields and never expose ledger UUIDs. `identification.number` on PIN payments created after public identification persistence is the partner-submitted public number (digits, no SINPE hyphens). Older payments may still return the hyphenated SINPE form from processing metadata. Use this as a webhook backup. Looks up a payment by either identifier: - `payment_id` (internal UUID) - `public_id` (numeric public identifier) For `client_reference`, use `POST /api/public/v1/transactions/payments/search`. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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. | | `payment_id` | path | string | yes | Internal payment UUID or numeric public_id. | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `amount` | object | yes | — | | `amount.amount` | string | yes | — | | `amount.currency` | string | yes | — | | `client_reference` | string | yes | — | | `created_at` | string | yes | — | | `destination` | object | yes | — | | `destination.account` | object | yes | — | | `destination.account.currency` | string | — | — | | `destination.account.type` | string | yes | — | | `destination.account.value` | string | yes | — | | `destination.identification` | object | yes | — | | `destination.identification.number` | string | yes | — | | `destination.identification.type` | string | yes | — | | `destination.name` | string | yes | — | | `error` | object | yes | — | | `error.domain` | string | yes | — | | `error.platform` | object | yes | — | | `error.platform.code` | string | yes | — | | `error.platform.message` | string | yes | — | | `external_reference` | string | yes | — | | `has_reversal` | boolean | yes | — | | `payment_id` | string | yes | — | | `payment_method_code` | string | yes | — | | `provider` | object | yes | — | | `provider.correlation_id` | string | yes | — | | `provider.occurred_at` | string | yes | — | | `provider.provider_status_code` | string | yes | — | | `provider.provider_status_desc` | string | yes | — | | `provider.provider_status_semantic` | string | yes | — | | `public_id` | string | yes | — | | `result` | string | yes | — | | `reversed_at` | string | yes | — | | `schema_version` | string | yes | — | | `source` | object | yes | — | | `source.account` | object | yes | — | | `source.account.currency` | string | — | — | | `source.account.type` | string | yes | — | | `source.account.value` | string | yes | — | | `source.identification` | object | yes | — | | `source.identification.number` | string | yes | — | | `source.identification.type` | string | yes | — | | `source.name` | string | yes | — | | `status` | string | yes | — | | `status_detail` | string | — | — | | `succeeded_at` | string | yes | — | | `type` | string | yes | — | | `updated_at` | string | yes | — | **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`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 404 | `PAYMENT_NOT_FOUND` | Payment not found. | | 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. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Validate external account for a payment method [#post-accounts-validate] ```http POST /api/public/v1/accounts/validate ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Validates a counterparty account (IBAN or phone) via the SINPE provider for the given payment method. Does not validate internal ledger 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 backing wallet operation (`ObtenerInfoMonedero`) does not return identification type or institution name. A successful validation **may** therefore return: - `data.holder.id_type` = `unknown` - `data.financial_institution.entity_name` = `""` (empty string) This is **expected, stable behavior**, not a mapping bug or a transient provider error. PIN (`ObtenerInformacionCuentaSinpe`) 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 `WebhookRejection.code` schema enum). **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`. **Cuenta / perfil / límites** | 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 | **Compensación con entidad destino** | 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 | Falló la acreditación en la cuenta destino | | 209 | Falló el débito en la cuenta origen | | 210 | Conciliación de la transacción no fue posible | **Identificación** | 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 | **Validación de formato / Core Bancario** | Code | Message | |---|---| | 1001 | Cuenta cliente activa | | 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 | **Canal / tipo de identificación** | 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 (monedero)** | 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 | ⚠️ This catalog is under review — some entries (notably `208` and `209`) are known to be pending verification against the official SINPE source and may be corrected in a future revision without notice. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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 ```json { "payment_method_code": "PIN", "account": { "type": "IBAN", "value": "CR16010400000000000031" } } ``` **Request example** — sinpe_movil ```json { "payment_method_code": "SINPE_MOVIL", "account": { "type": "PHONE", "value": "88887777" } } ``` **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `account` | object | yes | — | | `account.currency` | string | yes | — | | `account.product_name` | string | yes | — | | `account.type` | string | yes | — | | `account.value` | string | yes | — | | `financial_institution` | object | yes | — | | `financial_institution.country_code` | string | yes | — | | `financial_institution.entity_code` | string | yes | — | | `financial_institution.entity_name` | string | yes | — | | `holder` | object | yes | — | | `holder.id_number` | string | yes | — | | `holder.id_type` | string | yes | — | | `holder.name` | string | yes | — | | `is_valid` | boolean | yes | — | | `provider` | object | yes | — | | `provider.correlation_id` | string | — | — | | `provider.occurred_at` | string | — | — | | `provider.operation` | string | yes | — | | `rejection` | object | yes | — | | `rejection.code` | string | yes | — | | `rejection.message` | string | yes | — | | `rejection.reason_code` | string | yes | — | **Error responses** | HTTP | response_code | Description | |---|---|---| | 400 | `INVALID_REQUEST`, `INVALID_ACCOUNT_TYPE`, `INVALID_ACCOUNT_FORMAT`, `INVALID_PAYMENT_METHOD` | Invalid request. Check the required fields and try again. | | 401 | `UNAUTHORIZED` | Unauthorized. Verify your session or credentials. | | 403 | `FORBIDDEN`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 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. | --- # Banking API accounts > Banking API account operations: validation, listing, balances and statements. - kind: api-operation - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/cuentas The `Accounts` group covers the account as a resource: validate a destination account before transferring, list accessible accounts, check balances and request statements. ```http POST /api/public/v1/accounts/validate GET /api/public/v1/accounts GET /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 [#reglas] **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.** Generation is requested with a `POST` that accepts `Idempotency-Key` and returns a `request_id`; you then poll that `request_id` until the result is available. See [safe retries](/developers/api-bancario/convenciones#reintentos). **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 [#operaciones] ### List balances for accessible accounts [#get-accounts-balances] ```http GET /api/public/v1/accounts/balances ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. 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 | — | 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 | — | — | | `offset` | query | integer | — | — | | `type` | query | string | — | Optional filter by account **identifier scheme** (e.g. `IBAN`). Not the internal account classification. | | `value` | query | string | — | Optional filter by account identifier value (partial match supported for list endpoints). | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | — | | `items[].account` | object | yes | — | | `items[].account.currency` | string | — | — | | `items[].account.type` | string | yes | — | | `items[].account.value` | string | yes | — | | `items[].amounts` | object | yes | — | | `items[].amounts.available` | string | yes | — | | `items[].amounts.inbound_in_transit` | string | yes | — | | `items[].amounts.outbound_in_transit` | string | yes | — | | `items[].amounts.posted` | string | yes | — | | `items[].as_of` | string | yes | — | | `pagination` | object | yes | — | | `pagination.limit` | integer | yes | — | | `pagination.offset` | integer | yes | — | | `pagination.total` | integer | yes | — | **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`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 404 | `NOT_FOUND` | The requested resource was not found. | | 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 | — | Dependency error | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### List accessible accounts without balances [#get-accounts] ```http GET /api/public/v1/accounts ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. 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 | — | 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 | — | — | | `offset` | query | integer | — | — | | `type` | query | string | — | Optional filter by account **identifier scheme** (e.g. `IBAN`). Not the internal account classification. | | `value` | query | string | — | Optional filter by account identifier value (partial match supported for list endpoints). | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | — | | `items[].account` | object | yes | — | | `items[].account.currency` | string | — | — | | `items[].account.type` | string | yes | — | | `items[].account.value` | string | yes | — | | `pagination` | object | yes | — | | `pagination.limit` | integer | yes | — | | `pagination.offset` | integer | yes | — | | `pagination.total` | integer | yes | — | **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. | | 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 | — | Dependency error | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Get account balance [#post-accounts-balance] ```http POST /api/public/v1/accounts/balance ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. 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 | — | 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) | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `account` | object | yes | — | | `account.currency` | string | — | — | | `account.type` | string | yes | — | | `account.value` | string | yes | — | | `amounts` | object | yes | — | | `amounts.available` | string | yes | — | | `amounts.inbound_in_transit` | string | yes | — | | `amounts.outbound_in_transit` | string | yes | — | | `amounts.posted` | string | yes | — | | `as_of` | string | yes | — | **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`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 404 | `ACCOUNT_NOT_FOUND` | Account not found. | | 422 | `ACCOUNT_NOT_CONFIGURED`, `UNPROCESSABLE_ENTITY` | This account is not mapped in the system. Please contact support to configure it correctly. | | 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. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Request account statement generation [#post-accounts-statements] ```http POST /api/public/v1/accounts/statements ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. 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`. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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 | — | — | **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 | yes | RFC 3339 (max 60 days after date_from) | | `notify_email` | boolean | — | — | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `request_id` | string | yes | — | | `status` | string | yes | — | **202** — Statement request accepted. Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `request_id` | string (uuid) | yes | — | | `status` | string | yes | Values: PENDING, PROCESSING, DONE, ERROR | **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`, `ACCOUNT_ACCESS_DENIED` | You do not have permission to perform this action. | | 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. | | 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 | — | Dependency error | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Get account statement request status [#get-accounts-statements-request-id] ```http GET /api/public/v1/accounts/statements/{request_id} ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Returns request status. When `DONE`, includes a GCS signed `download_url` (48h from generation) and `expires_at`. When `ERROR`, includes a generic `message` without internal details. Unknown or unauthorized `request_id` returns 404. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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 | — | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `download_url` | string | — | — | | `expires_at` | string | — | — | | `message` | string | — | — | | `request_id` | string | yes | — | | `status` | string | yes | — | **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. | | 429 | `TOO_MANY_REQUESTS` | Too many requests. Please retry after a short delay. | | 500 | `INTERNAL_ERROR` | An unexpected error occurred. Please try again later. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | --- # Banking API webhooks > Destination registration, subscribable features and payload contracts for Banking API webhooks. - kind: api-operation - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/webhooks The `Webhooks` group lets you register the HTTPS destination where you receive events, see which features you can subscribe to per payment method and fetch each event's payload contract before writing the receiver. ```http POST /api/public/v1/webhook/destination GET /api/public/v1/webhook/features GET /api/public/v1/webhook/event-contracts/{event_type} ``` ## Events [#eventos] Our API notifies you of four event types: | Event | When it arrives | |---|---| | `payin.posted` | An inbound movement was credited. | | `payment.succeeded` | The payment finished successfully. | | `payment.failed` | The payment finished with a failure. | | `payment.reversed` | The payment was reversed. | A reversed payment keeps the `posted` state and is told apart by `result: reversed` and `has_reversal: true`; see [payment states](/developers/api-bancario/pagos#estados). ## Receiver best practices [#receptor] Respond `2xx` as soon as you receive the event and process it separately: a slow receiver causes redeliveries. Treat events as potentially repeated and deduplicate by the payment identifier. And do not trust the final state to the webhook alone: confirm it with [the payment lookup](/developers/api-bancario/pagos#operaciones). ## Operations [#operaciones] ### Create webhook destination [#post-webhook-destination] ```http POST /api/public/v1/webhook/destination ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Valid fields depend on `destination_kind` and `scope_kind`. Send only the fields that apply to your combination; other scope fields must be omitted (not null placeholders). **Event payloads** Inspect outbound contracts with `GET /api/public/v1/webhook/event-contracts/{event_type}` (`payin.posted`, `payment.succeeded`, `payment.failed`, `payment.reversed`). **destination_kind** - `EXTERNAL_WEBHOOK` (only kind available on the public API): requires `webhook_url` (must start with `https://`) and `secret_ref`. Optional delivery tuning (`timeout_ms`, `max_attempts`). Payloads are always encrypted with `AES_GCM` using `secret_ref`; the HTTP body is `{"data":""}`. - `INTERNAL` is reserved for platform administration and cannot be created through this endpoint. **scope_kind** - `TENANT`: omit `scope_account_id`, `scope_owner_type`, and `scope_owner_id`. - `ACCOUNT`: require `scope_account_id`; omit owner fields. - `OWNER`: require `scope_owner_type` and `scope_owner_id`; omit `scope_account_id`. `scope_owner_type` is one of `tenant`, `partner`, `user`, `platform`, `customer` (lowercase in payload). ## Webhook encryption (EXTERNAL_WEBHOOK) Deliveries always send `Content-Type: application/json` and `Accept: application/json`. The HTTP body is always `{"data": ""}`. The base64-decoded bytes are: ``` 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 additional authenticated data (AAD) is used. `plaintext` is the same JSON envelope documented per event type in `GET /webhook/event-contracts/{event_type}` — decrypt first, then apply that contract. Authenticity is provided by the GCM auth tag (decryption fails if the body was tampered with). Deduplicate retries using `event.event_id` in the payload envelope — delivery retries can redeliver the same event with the same `event_id`. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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 | |---|---|---|---| | `feature_id` | string (uuid) | yes | Feature to subscribe (from `GET /api/public/v1/webhook/features`). Must exist or create fails. | | `is_enabled` | boolean | — | Defaults to true if omitted. | | `destination_kind` | string | yes | Public API accepts only `EXTERNAL_WEBHOOK` (HTTPS callback with shared secret). `INTERNAL` destinations are managed administratively. Values: EXTERNAL_WEBHOOK | | `destination_ref` | string | — | Not used on the public API. Reserved for internal destinations. | | `webhook_url` | string (uri) | — | Required (must use `https://`). | | `secret_ref` | string | — | Required shared secret used to decrypt AES_GCM payloads (`key = SHA256(secret_ref)`). See "Webhook encryption" in this operation's description. | | `signature_algo` | string | — | Fixed to `AES_GCM` for `EXTERNAL_WEBHOOK`. Omitted on create; stored as `AES_GCM`. `HMAC_SHA256` is no longer supported. Values: AES_GCM | | `replay_window_sec` | integer | — | Reserved for legacy destinations. Not used for `EXTERNAL_WEBHOOK` AES_GCM deliveries. | | `timeout_ms` | integer | — | HTTP timeout for delivery attempts (`EXTERNAL_WEBHOOK`). | | `max_attempts` | integer | — | Max delivery attempts (`EXTERNAL_WEBHOOK`). | | `backoff_policy` | object | — | Optional free-form JSON retry/backoff override, stored as-is and not currently validated or documented by the platform. Omit to use the platform's default backoff policy. | | `scope_kind` | string | yes | - `TENANT`: no scope id fields. - `ACCOUNT`: set `scope_account_id` only. - `OWNER`: set `scope_owner_type` and `scope_owner_id` only. Values: TENANT, ACCOUNT, OWNER | | `scope_account_id` | string (uuid) | — | Required when `scope_kind` is `ACCOUNT`. Must be omitted for `TENANT` and `OWNER`. | | `scope_owner_type` | string | — | Required when `scope_kind` is `OWNER`. Allowed values (lowercase) `tenant`, `partner`, `user`, `platform`, `customer`. Omit for `TENANT` and `ACCOUNT`. | | `scope_owner_id` | string (uuid) | — | Required when `scope_kind` is `OWNER`. Omit for `TENANT` and `ACCOUNT`. | **Request example** — external_webhook_tenant ```json { "feature_id": "11111111-1111-1111-1111-111111111111", "destination_kind": "EXTERNAL_WEBHOOK", "scope_kind": "TENANT" } ``` **201** — The resource was created successfully. `response_code`: `CREATED` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `created_at` | string | — | — | | `destination_kind` | string | yes | — | | `destination_ref` | string | — | — | | `feature_id` | string | yes | — | | `id` | string | yes | — | | `is_enabled` | boolean | yes | — | | `max_attempts` | integer | — | — | | `replay_window_sec` | integer | yes | — | | `scope_account_id` | string | — | — | | `scope_kind` | string | yes | — | | `scope_owner_id` | string | — | — | | `scope_owner_type` | string | — | — | | `secret_ref` | string | — | — | | `signature_algo` | string | yes | — | | `timeout_ms` | integer | — | — | | `updated_at` | string | — | — | | `webhook_url` | string | — | — | **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. | | 429 | `TOO_MANY_REQUESTS` | Too many requests. Please retry after a short delay. | | 500 | `INTERNAL_ERROR` | An unexpected error occurred. Please try again later. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### List webhook features by payment method [#get-webhook-features] ```http GET /api/public/v1/webhook/features ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Returns feature IDs and the `event_types` each feature can emit. Tenant scope is taken from the access token; clients must not send `tenant_id`. **Naming:** `event_types` use `payment.*` for resource-level outcomes (`payment.succeeded`, `payment.failed`, `payment.reversed`) and `payin.posted` for the inbound direction only (funds received). There is no `payout.posted` and no `transaction.*` events. The delivered JSON object is always `payment`. Inspect payload shapes with `GET /api/public/v1/webhook/event-contracts/{event_type}` (`payin.posted`, `payment.succeeded`, `payment.failed`, `payment.reversed`). **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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. | | `country_code` | query | string | — | — | | `payment_method_code` | query | string | — | — | | `webhook_enabled` | query | boolean | — | — | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | — | | `items[].event_types` | array | yes | — | | `items[].id` | string | yes | — | **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. | | 429 | `TOO_MANY_REQUESTS` | Too many requests. Please retry after a short delay. | | 500 | `INTERNAL_ERROR` | An unexpected error occurred. Please try again later. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ### Webhook event contract [#get-webhook-event-contracts-event-type] ```http GET /api/public/v1/webhook/event-contracts/{event_type} ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Returns the outbound webhook payload contract for one public event type (`schema_version` 1.0.0). The envelope object is always `payment` (never `payin`, `payout`, or `transaction`). **When to use each name** - **payment** — resource and envelope. Outcome events: `payment.succeeded`, `payment.failed`, `payment.reversed` (both PAYIN and PAYOUT). Correlate with REST `/transactions/payments`. - **payin / payout** — direction (`PAYIN` inbound, `PAYOUT` outbound). `payin.posted` is inbound-only (ledger posted / funds received). There is no public `payout.posted`. - **transaction** — REST path prefix only. Not used in webhook `event_type` or payload objects. Supported `event_type` values: - `payin.posted` — inbound (PAYIN) payment reached public status `posted`. Envelope field is `payment`. `payment.local_payment` is a JSON boolean (`true`/`false`), not a string. - `payment.succeeded` — the provider confirmed success for a payment (PAYIN or PAYOUT; PIN or SINPE Móvil). Resource-level outcome, not direction-specific. - `payment.failed` — processing ended in a definitive failure (public `status=failed`), with structured `payment.error` (`domain` / `platform`). When the provider rejected the payment, the envelope also includes `rejection`. Not used for later reversals. - `payment.reversed` — subsequent reversal of a payment that already progressed (typically `status=posted` or `confirmed`). REST `result=reversed` and `has_reversal=true`. Processing `status` is unchanged. Not a `payment.failed`. Unknown `event_type` values (including internal-only `payment.status_changed`) return `404`. ## SINPE rejection codes (`rejection`, only on `payment.failed`) When `event_type = payment.failed` and the provider rejected the payment, the envelope includes `rejection` (same object and catalog as `data.rejection` on `POST /accounts/validate`). `rejection.code` is one of a fixed set of normalized semantics (see the `WebhookRejection.code` schema enum). **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`). 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`. **Cuenta / perfil / límites** | 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 | **Compensación con entidad destino** | 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 | Falló la acreditación en la cuenta destino | | 209 | Falló el débito en la cuenta origen | | 210 | Conciliación de la transacción no fue posible | **Identificación** | 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 | **Validación de formato / Core Bancario** | Code | Message | |---|---| | 1001 | Cuenta cliente activa | | 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 | **Canal / tipo de identificación** | 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 (monedero)** | 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 | ⚠️ This catalog is under review — some entries (notably `208` and `209`) are known to be pending verification against the official SINPE source and may be corrected in a future revision without notice. Delivery body is JSON. `EXTERNAL_WEBHOOK` HTTP body is always `{"data":""}` (AES-256-GCM, key = SHA256(secret_ref)). Decrypt to obtain the event envelope documented per `GET /webhook/event-contracts/{event_type}`. and the decrypted plaintext matches this contract. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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. | | `event_type` | path | string | yes | Values: payin.posted, payment.succeeded, payment.failed, payment.reversed | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `description` | string | yes | — | | `event_type` | string | yes | — | | `example` | object | yes | — | | `schema_version` | string | yes | — | **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. | | 429 | `TOO_MANY_REQUESTS` | Too many requests. Please retry after a short delay. | | 500 | `INTERNAL_ERROR` | An unexpected error occurred. Please try again later. | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | ## Payload contracts [#contratos] Each event has its own data structure. This is what we send you in each one: ### payin.posted [#contrato-payin-posted] Data we send for `payin.posted`. | Field | Type | Required | Description | |---|---|---|---| | `schema_version` | string | yes | — | | `event` | object | yes | — | | `event.event_id` | string (uuid) | yes | — | | `event.event_type` | string | yes | Values: payin.posted | | `event.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `event.correlation_id` | string | yes | — | | `context` | object | yes | — | | `context.country_code` | string | yes | — | | `context.payment_method_code` | string | yes | Public payment method catalog. Same values as REST (`PIN`, `SINPE_MOVIL`). Internal codes such as `PM_PIN` are never exposed. Values: PIN, SINPE_MOVIL | | `payment` | object | yes | — | | `payment.payment_id` | string (uuid) | yes | — | | `payment.public_id` | integer (int64) | yes | — | | `payment.status` | string | yes | Public payment status catalog. Always lowercase. Same values on REST (create, list, get) and webhooks (`payment.status`). Internal lifecycle states are collapsed: - `pending`: initiated, validated, accepted - `processing`: processing, pending_processing, posting - `confirmed`: confirmed by the rail, not yet ledger-posted - `posted`: ledger posted (stays `posted` after a later reversal) - `failed`: failed, rejected, posting_failed, cancelled, expired A reversal is **not** a status. Use REST `result=reversed` / `has_reversal=true` and webhook `payment.reversed`. Operational detail remains in `status_detail` (uppercase internal name) on REST create/list/get. Webhook `payment.failed` uses `error.platform.code` to distinguish posting failures from provider rejections. Values: pending, processing, confirmed, posted, failed | | `payment.amount` | object | yes | — | | `payment.amount.amount` | string | — | — | | `payment.amount.currency` | string | — | — | | `payment.posted_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `payment.account` | object | yes | — | | `payment.account.type` | string | yes | — | | `payment.account.value` | string | yes | — | | `payment.reference` | object | yes | — | | `payment.reference.client_reference` | string | yes | Partner reference sent at payment creation (`client_reference`), not `detail_reference`. | | `payment.reference.external_reference` | string | yes | — | | `payment.destination_phone_number` | string | — | — | | `payment.local_payment` | boolean | — | JSON boolean (`true`/`false`), not the strings `"true"`/`"false"`. | | `payment.origin_client_name` | string | — | — | | `provider` | object | yes | Shared provider snapshot on REST payment reads and webhook envelopes. Rail reference is only on `payment.external_reference`, not duplicated here. `provider_status_*` match webhook `rejection.reason_code` / `message` / `code` when the provider rejected the operation. `null` when there is no rejection. | | `provider.correlation_id` | string | yes | Transaction correlation UUID (`execution.correlation_id` / `metadata.tx.correlationId`). Sent to GX/SINPE as `correlationId`. Not `channel_reference` (e.g. `MOBILE_APP`). Distinct from webhook `event.correlation_id` (HTTP request id for this delivery). `null` when unknown. | | `provider.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `provider.provider_status_code` | string | yes | Raw provider Motivo/code (e.g. `31`). Same meaning as webhook `rejection.reason_code`. `null` when there is no provider rejection. | | `provider.provider_status_desc` | string | yes | Provider Motivo text (prefers Detalle). Same meaning as webhook `rejection.message`. `null` when there is no provider rejection. | | `provider.provider_status_semantic` | string | yes | Normalized platform semantic (e.g. `ACCOUNT_BLOCKED`). Same meaning as webhook `rejection.code`. `null` when there is no provider rejection. | ### payment.succeeded [#contrato-payment-succeeded] Data we send for `payment.succeeded`. | Field | Type | Required | Description | |---|---|---|---| | `schema_version` | string | yes | — | | `event` | object | yes | — | | `event.event_id` | string (uuid) | yes | — | | `event.event_type` | string | yes | Values: payment.succeeded | | `event.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `event.correlation_id` | string | yes | — | | `context` | object | yes | — | | `context.country_code` | string | yes | — | | `context.payment_method_code` | string | yes | Public payment method catalog. Same values as REST (`PIN`, `SINPE_MOVIL`). Internal codes such as `PM_PIN` are never exposed. Values: PIN, SINPE_MOVIL | | `payment` | object | yes | — | | `payment.payment_id` | string (uuid) | yes | — | | `payment.public_id` | integer (int64) | yes | — | | `payment.status` | string | yes | Public payment status catalog. Always lowercase. Same values on REST (create, list, get) and webhooks (`payment.status`). Internal lifecycle states are collapsed: - `pending`: initiated, validated, accepted - `processing`: processing, pending_processing, posting - `confirmed`: confirmed by the rail, not yet ledger-posted - `posted`: ledger posted (stays `posted` after a later reversal) - `failed`: failed, rejected, posting_failed, cancelled, expired A reversal is **not** a status. Use REST `result=reversed` / `has_reversal=true` and webhook `payment.reversed`. Operational detail remains in `status_detail` (uppercase internal name) on REST create/list/get. Webhook `payment.failed` uses `error.platform.code` to distinguish posting failures from provider rejections. Values: pending, processing, confirmed, posted, failed | | `payment.amount` | object | yes | — | | `payment.amount.amount` | string | — | — | | `payment.amount.currency` | string | — | — | | `payment.client_reference` | string | yes | Partner reference sent at payment creation (`client_reference`), not `detail_reference`. | | `payment.external_reference` | string | yes | — | | `payment.fee_total` | string | yes | Fee total as decimal string (same currency as amount.currency) | | `payment.tax_total` | string | yes | Tax total as decimal string (same currency as amount.currency) | | `payment.net_amount` | string | yes | Net cash impact on the merchant account. PAYIN: amount - fee_total - tax_total (what the merchant receives). PAYOUT: amount + fee_total + tax_total (total debit; fee is charged separately and is not deducted from the transferred amount). | | `payment.succeeded_at` | string | yes | Timestamp when the provider confirmed the payment succeeded. | | `provider` | object | yes | Shared provider snapshot on REST payment reads and webhook envelopes. Rail reference is only on `payment.external_reference`, not duplicated here. `provider_status_*` match webhook `rejection.reason_code` / `message` / `code` when the provider rejected the operation. `null` when there is no rejection. | | `provider.correlation_id` | string | yes | Transaction correlation UUID (`execution.correlation_id` / `metadata.tx.correlationId`). Sent to GX/SINPE as `correlationId`. Not `channel_reference` (e.g. `MOBILE_APP`). Distinct from webhook `event.correlation_id` (HTTP request id for this delivery). `null` when unknown. | | `provider.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `provider.provider_status_code` | string | yes | Raw provider Motivo/code (e.g. `31`). Same meaning as webhook `rejection.reason_code`. `null` when there is no provider rejection. | | `provider.provider_status_desc` | string | yes | Provider Motivo text (prefers Detalle). Same meaning as webhook `rejection.message`. `null` when there is no provider rejection. | | `provider.provider_status_semantic` | string | yes | Normalized platform semantic (e.g. `ACCOUNT_BLOCKED`). Same meaning as webhook `rejection.code`. `null` when there is no provider rejection. | ### payment.failed [#contrato-payment-failed] Data we send for `payment.failed`. | Field | Type | Required | Description | |---|---|---|---| | `schema_version` | string | yes | — | | `event` | object | yes | — | | `event.event_id` | string (uuid) | yes | — | | `event.event_type` | string | yes | Values: payment.failed | | `event.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `event.correlation_id` | string | yes | — | | `context` | object | yes | — | | `context.country_code` | string | yes | — | | `context.payment_method_code` | string | yes | Public payment method catalog. Same values as REST (`PIN`, `SINPE_MOVIL`). Internal codes such as `PM_PIN` are never exposed. Values: PIN, SINPE_MOVIL | | `payment` | object | yes | — | | `payment.payment_id` | string (uuid) | yes | — | | `payment.public_id` | integer (int64) | yes | — | | `payment.status` | string | yes | Public payment status catalog. Always lowercase. Same values on REST (create, list, get) and webhooks (`payment.status`). Internal lifecycle states are collapsed: - `pending`: initiated, validated, accepted - `processing`: processing, pending_processing, posting - `confirmed`: confirmed by the rail, not yet ledger-posted - `posted`: ledger posted (stays `posted` after a later reversal) - `failed`: failed, rejected, posting_failed, cancelled, expired A reversal is **not** a status. Use REST `result=reversed` / `has_reversal=true` and webhook `payment.reversed`. Operational detail remains in `status_detail` (uppercase internal name) on REST create/list/get. Webhook `payment.failed` uses `error.platform.code` to distinguish posting failures from provider rejections. Values: pending, processing, confirmed, posted, failed | | `payment.amount` | object | yes | — | | `payment.amount.amount` | string | — | — | | `payment.amount.currency` | string | — | — | | `payment.client_reference` | string | — | Partner reference sent at payment creation (`client_reference`), not `detail_reference`. | | `payment.external_reference` | string | — | — | | `payment.error` | object | yes | — | | `payment.error.domain` | string | yes | Values: provider_rejection, platform_validation, platform_posting, platform_infra, platform_reversal, sinpe_rejection | | `payment.error.platform` | object | yes | — | | `provider` | object | yes | Shared provider snapshot on REST payment reads and webhook envelopes. Rail reference is only on `payment.external_reference`, not duplicated here. `provider_status_*` match webhook `rejection.reason_code` / `message` / `code` when the provider rejected the operation. `null` when there is no rejection. | | `provider.correlation_id` | string | yes | Transaction correlation UUID (`execution.correlation_id` / `metadata.tx.correlationId`). Sent to GX/SINPE as `correlationId`. Not `channel_reference` (e.g. `MOBILE_APP`). Distinct from webhook `event.correlation_id` (HTTP request id for this delivery). `null` when unknown. | | `provider.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `provider.provider_status_code` | string | yes | Raw provider Motivo/code (e.g. `31`). Same meaning as webhook `rejection.reason_code`. `null` when there is no provider rejection. | | `provider.provider_status_desc` | string | yes | Provider Motivo text (prefers Detalle). Same meaning as webhook `rejection.message`. `null` when there is no provider rejection. | | `provider.provider_status_semantic` | string | yes | Normalized platform semantic (e.g. `ACCOUNT_BLOCKED`). Same meaning as webhook `rejection.code`. `null` when there is no provider rejection. | | `rejection` | object | — | Rail-agnostic provider rejection. Shared by `POST /accounts/validate` (`data.rejection`) and the `payment.failed` webhook (`rejection`). Object only when the provider rejected the operation; omitted/`null` otherwise. | | `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. | ### payment.reversed [#contrato-payment-reversed] Data we send for `payment.reversed`. | Field | Type | Required | Description | |---|---|---|---| | `schema_version` | string | yes | — | | `event` | object | yes | — | | `event.event_id` | string (uuid) | yes | — | | `event.event_type` | string | yes | Values: payment.reversed | | `event.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `event.correlation_id` | string | yes | — | | `context` | object | yes | — | | `context.country_code` | string | yes | — | | `context.payment_method_code` | string | yes | Public payment method catalog. Same values as REST (`PIN`, `SINPE_MOVIL`). Internal codes such as `PM_PIN` are never exposed. Values: PIN, SINPE_MOVIL | | `payment` | object | yes | — | | `payment.payment_id` | string (uuid) | yes | — | | `payment.public_id` | integer (int64) | yes | — | | `payment.status` | string | yes | Public payment status catalog. Always lowercase. Same values on REST (create, list, get) and webhooks (`payment.status`). Internal lifecycle states are collapsed: - `pending`: initiated, validated, accepted - `processing`: processing, pending_processing, posting - `confirmed`: confirmed by the rail, not yet ledger-posted - `posted`: ledger posted (stays `posted` after a later reversal) - `failed`: failed, rejected, posting_failed, cancelled, expired A reversal is **not** a status. Use REST `result=reversed` / `has_reversal=true` and webhook `payment.reversed`. Operational detail remains in `status_detail` (uppercase internal name) on REST create/list/get. Webhook `payment.failed` uses `error.platform.code` to distinguish posting failures from provider rejections. Values: pending, processing, confirmed, posted, failed | | `payment.result` | string | yes | Values: reversed | | `payment.has_reversal` | boolean | yes | Values: true | | `payment.amount` | object | yes | — | | `payment.amount.amount` | string | — | — | | `payment.amount.currency` | string | — | — | | `payment.client_reference` | string | — | — | | `payment.external_reference` | string | — | — | | `payment.reversed_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `payment.reason` | object | — | — | | `payment.reason.code` | string | — | — | | `payment.reason.message` | string | — | — | | `provider` | object | yes | Shared provider snapshot on REST payment reads and webhook envelopes. Rail reference is only on `payment.external_reference`, not duplicated here. `provider_status_*` match webhook `rejection.reason_code` / `message` / `code` when the provider rejected the operation. `null` when there is no rejection. | | `provider.correlation_id` | string | yes | Transaction correlation UUID (`execution.correlation_id` / `metadata.tx.correlationId`). Sent to GX/SINPE as `correlationId`. Not `channel_reference` (e.g. `MOBILE_APP`). Distinct from webhook `event.correlation_id` (HTTP request id for this delivery). `null` when unknown. | | `provider.occurred_at` | string (date-time) | yes | RFC 3339 timestamp in UTC with second precision and a `Z` suffix. Fractional seconds are never emitted. Example: `2026-01-15T12:00:00Z`. | | `provider.provider_status_code` | string | yes | Raw provider Motivo/code (e.g. `31`). Same meaning as webhook `rejection.reason_code`. `null` when there is no provider rejection. | | `provider.provider_status_desc` | string | yes | Provider Motivo text (prefers Detalle). Same meaning as webhook `rejection.message`. `null` when there is no provider rejection. | | `provider.provider_status_semantic` | string | yes | Normalized platform semantic (e.g. `ACCOUNT_BLOCKED`). Same meaning as webhook `rejection.code`. `null` when there is no provider rejection. | --- # Banking API catalogs > Customer identification type catalog per country in the Banking API. - kind: api-operation - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/catalogos The `Catalogs` group publishes the values your integration must use in customer identification fields, instead of hardcoding them. ```http GET /api/public/v1/catalogs/customer-identification-types ``` Filter by two-letter `country_code` to get the types in force in that country. Fetch the catalog when you start the integration and cache the result; read it again before adding a new country. ## Operations [#operaciones] ### List customer identification person types [#get-catalogs-customer-identification-types] ```http GET /api/public/v1/catalogs/customer-identification-types ``` Host: `https://api-baas-sandbox.tilopay.com` — requires `Authorization: Bearer `. Returns the distinct customer person types (`individual`, `business`, `both`) available for the authenticated tenant, optionally filtered by country. This is a person-type classification, not the payment identification-document catalog (`cedula`, `juridica`, `dimex`, ...) used by `identification.type` on `POST /payments`. **Parameters** | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `X-Correlation-Id` | header | string | — | 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. | | `country_code` | query | string | — | Optional ISO-2 country code used to filter results. A legacy `country` query parameter is also accepted as an alias when `country_code` is omitted; prefer `country_code`. | **200** — The request was processed successfully. `response_code`: `OK` Fields in `data`: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | — | | `items[].code` | string | yes | — | **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. | | 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 | — | Dependency error | | 503 | `SERVICE_UNAVAILABLE` | A required service is temporarily unavailable. Please try again later. | --- # Banking API response codes > Full response_code catalog for Tilopay's Banking API and how to react to each family. - kind: concept - status: stable - api_version: 1.0.0 - last_verified: 2026-09-02 - url: https://tilopay.com/en/developers/api-bancario/errores Every Banking API error arrives in the [standard envelope](/developers/api-bancario/convenciones#envelope), with `success: false`, the HTTP status in `http_status_code` and a `response_code` from the catalog. Branch on `response_code`. ## How to react per family [#familias] **Request `4xx`.** The body or parameters are invalid, or the resource does not exist. Fix the call before resending it: retrying as-is returns the same error. This covers `INVALID_REQUEST`, `VALIDATION_ERROR`, `NOT_FOUND` and business codes such as `INSUFFICIENT_FUNDS` or `LIMIT_EXCEEDED`. **`401` and `403`.** The token is absent, expired, or its context does not authorize the resource. Renew the access token with the [token exchange](/developers/api-bancario/autenticacion#exchange); if it persists with a fresh token, that context has no permission over the resource. **`409 IDEMPOTENCY_CONFLICT`.** You reused an `Idempotency-Key` with a different body. Use a new key for the new intent; see [safe retries](/developers/api-bancario/convenciones#reintentos). **`429`.** You exceeded the call limit. Space out requests and retry with backoff. **`5xx`.** Service-side failure. Retry with backoff, and on `POST` calls resend the **same** `Idempotency-Key` so the record is not duplicated. Keep the `correlation_id` before escalating to support. ## Full catalog [#catalogo] These are the 28 `ResponseCode` values our API returns, with the HTTP statuses they appear under. `OK` is the code of successful responses. | response_code | HTTP status returned | |---|---| | `OK` | 200 | | `CREATED` | 201 | | `NO_CONTENT` | — | | `UNAUTHORIZED` | 401 | | `FORBIDDEN` | 403 | | `TOO_MANY_REQUESTS` | 429 | | `SERVICE_UNAVAILABLE` | 503 | | `INTERNAL_ERROR` | 500 | | `INVALID_REQUEST` | 400 | | `ACCOUNT_ACCESS_DENIED` | 403 | | `PROVIDER_ERROR` | 502 | | `INVALID_CREDENTIALS` | 401 | | `NOT_FOUND` | 404 | | `ACCOUNT_NOT_FOUND` | 404 | | `ACCOUNT_NOT_CONFIGURED` | 422 | | `UNPROCESSABLE_ENTITY` | 422 | | `PAYMENT_NOT_FOUND` | 404 | | `INVALID_ACCOUNT_TYPE` | 400 | | `INVALID_ACCOUNT_FORMAT` | 400 | | `INVALID_PAYMENT_METHOD` | 400 | | `PAYMENT_INVALID_PAYLOAD` | 400 | | `ACCOUNT_INVALID` | 400 | | `ACCOUNT_HOLDER_MISMATCH` | 400 | | `IDEMPOTENCY_CONFLICT` | 409 | | `PAYMENT_DUPLICATE` | 409 | | `CONFLICT` | 409 | | `INSUFFICIENT_FUNDS` | 422 | | `LIMIT_EXCEEDED` | 422 | A `response_code` outside this list is not part of the API's 1.0.0 contract: treat it as a generic error based on its HTTP status and report it with the `correlation_id`. --- # AI agents > The three planes Tilopay offers an AI agent: reading the documentation, operating the account through the MCP server, and integrating with the API and the SDK. - kind: guide - status: stable - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/agentes ## Read [#leer] The whole portal is available in machine-readable form: the [`llms.txt`](https://tilopay.com/en/llms.txt) index, the raw Markdown of any URL on the site by adding `.md`, the API [`openapi.json`](/developers/openapi.json) and the MCP server [`mcp.json`](/developers/mcp.json). ## Automate your business operations [#actuar] The [MCP server](/en/developers/agentes/mcp) lets an assistant operate your Tilopay account: query sales, create payment links, issue refunds. Access is self-service — you sign in with your Tilopay credentials — and your credentials never leave the Tilopay server. ## Integrate into a product [#integrar] When you are building a product that charges, the path is the [JavaScript SDK](/en/developers/sdk) and the [Acquiring API](/en/developers/api/autenticacion), not the MCP. ## Which one to choose [#cual-elegir] - Operate the account from an assistant → [MCP server](/en/developers/agentes/mcp). - Build a product that charges → [API](/en/developers/api/autenticacion) and [SDK](/en/developers/sdk). - Feed an agent with the documentation → [machine-readable documentation](/en/developers/agentes/documentacion-legible). MCP access is self-service: install the connector and sign in with your Tilopay credentials, as the [MCP server page](/en/developers/agentes/mcp) explains. --- # Tilopay Assistant on WhatsApp > What the Tilopay WhatsApp assistant is, how it is enabled, how to use it and what it can do: sales, transactions, payment links, catalog, micro-store and orders, receipts, subscriptions, saved cards, reminders and alerts. - kind: guide - status: stable - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/asistente-whatsapp ## What it is [#que-es] The Tilopay assistant on WhatsApp is the same Tilopay MCP server wired to a WhatsApp chat. Instead of configuring an MCP client, you write to it in plain language from the WhatsApp they already use, and the assistant runs the tools against their Tilopay account. The capabilities are the ones the MCP exposes: whatever an agent can do through the [acquiring tools](/developers/agentes/mcp/adquirencia) and, on request, the [banking tools](/developers/agentes/mcp/api-bancario), the assistant can do from the chat. It is not a canned-response chatbot: it reads and operates on real account data, and answers in plain text meant to be read on a phone. ## How it is enabled [#activacion] Linking takes place from the WhatsApp chat itself and takes about a minute. You must be affiliated with Tilopay, and the person needs their Tilopay email and password. 1. Send any message to Tilopay's official number, **+506 7098 2131**, or open the chat directly: [Open WhatsApp chat](https://wa.me/50670982131). If the number is not linked yet, the chat shows a welcome message with a **Vincular mi cuenta** button. You can also write “vincular mi cuenta” to request the button again. 2. Tap **Vincular mi cuenta**. Enter credentials and codes in the secure form that opens, never as messages in the chat. 3. Enter the same email and password you use to sign in to the Tilopay dashboard. 4. If your account has two-step verification, enter the code Tilopay sends to the method shown on the screen. 5. Enter the 6-digit code sent to your Tilopay email. This final code is always required to confirm the link. 6. If your user has more than one merchant, choose the one you want to use. When finished, close the form; you can now ask the assistant to query or operate on that account. Linking is authenticated with the email, the password and the code sent to the email, not with the phone number: you can text from any phone. +506 7098 2131 is also the number Tilopay sends verification codes from; save it as a contact. An unlinked number cannot run queries or operations. The linking welcome is sent at most once per day; even if it does not appear again that day, you can request the button by writing “vincular mi cuenta.” ## Link the account again [#revincular] If the session stops working or the password changes, write “vincular mi cuenta” and complete the same process. Linking the same number again updates the existing connection: it does not create another profile or disconnect MCP clients that are already linked. Each channel keeps its own session, so a problem in WhatsApp does not automatically close sessions in Claude, ChatGPT, Cursor or other MCP clients. ## How to use it [#uso] There are no commands or menus. You write as you would to a teammate: - "how are sales going over the last 30 days?" - "charge Ana 25 thousand colones for the appointment and send it to me on WhatsApp" - "look up order 10432" - "every day at 8pm send me today's sales summary" The assistant remembers the last messages of the thread, so you can keep going without repeating data ("send it by email too", "delete that link"). ## What it can do [#capacidades] | Area | What it solves from the chat | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Sales | Summaries and trends by day, week or month, plus analysis with average ticket, approval rate, top customers and decline reasons. | | Transactions | List and look up transactions, and capture, refund or reverse a charge. | | Payment links | Create a link, share it on WhatsApp or by email, see its detail and delete it. | | Catalog | List products or services, see their detail, create, change or delete an item. | | Micro-store | Build a store with a cart from the catalog products, list the active stores and deactivate one. | | Orders | Turn on order management with the store name and logo, see the summary, list orders by status, see the paid but undelivered ones and mark them as delivered. | | Receipts | List the receipts of approved charges and resend a customer the receipt for their payment. | | Contacts | Save, list and delete contacts so you can charge them by name. | | Recurring charges | Create, edit and delete plans, build them step by step from the chat, generate the subscription link, review subscribers and their payments, edit, pause, reactivate or delete a subscriber, see failed charges, generate the link to settle a declined charge and manage coupons. | | Saved cards | Tokenize a customer's card, list or delete their cards, check payment methods and installments, charge a saved card and review groups, affiliates and bulk charges. | | Reminders | Schedule one-off, daily, weekly or monthly reminders that can also run an assistant query when they fire; list them and cancel them. Up to 20 active. | | Alerts | Turn your alerts on and off and review them: approved charges, declined charges, orders and the daily summary at the chosen time. | | Profile and recipes | Save the business profile and get automation recommendations based on what it sells. | | Support | Find the official guide for a topic, run the connection diagnostics and review an integration error. | | Banking (on request) | Accounts, balances, payments, statements and movement analysis; and, with email confirmation, validate a destination account, send a transfer and check its status. Only with banking API credentials. | There are 117 tools across 15 groups today. Parameter detail and the access level of each one live in the [MCP server](/developers/agentes/mcp) pages. The assistant also accepts **images** in the chat: a product photo for the catalog, or the logo shown in the micro-store and on receipts. ## Authorizing sensitive operations [#autorizacion] Operations that move money or alter charges — refunds, captures, reversals, saved-card charges, subscriber changes, deleting links, products or plans, and sending a bank transfer — ask for a 6-digit code that Tilopay sends to the user's registered email. Without that code the assistant does not run them. The merchant's keys never travel through the chat, and the assistant never reveals credentials, tokens or internal identifiers. Every query and operation is audited with user, tool, date and result, with per-account usage limits. ## Messages the assistant sends on its own [#avisos] - **Payment received:** when a customer pays a link created from the assistant, a WhatsApp message arrives with amount, customer, description, reference and order number. - **Scheduled reports:** the summary or query you scheduled, with the data pulled at send time. - **Reminders:** the reminder you scheduled, with the query result attached if you asked for an action. Reminders about charges or links always go to your WhatsApp, never to the customer. - **Pending orders:** the daily reminder of paid orders still to be delivered, when order management is on. ## If you prefer your own agent [#mcp] The same tools are available over MCP for Claude, ChatGPT, Cursor or any other client: start with [Connect your agent](/developers/agentes/mcp/conectar) and [Permissions](/developers/agentes/mcp/permisos). --- # MCP server > How to connect an MCP client to the Tilopay server with OAuth and the same Tilopay account credentials. - kind: mcp-index - status: stable - last_verified: 2026-09-23 - url: https://tilopay.com/en/developers/agentes/mcp ## How access works [#acceso] Access is self-service: there is no registration form and no manual approval. You installs the connector with the server URL and, in the authorization window, signs in with the **same credentials as their Tilopay account** (email and password) and confirms the verification code Tilopay sends. That automatically creates the assistant user, ties it to you, and registers your API credentials encrypted on the server side. Three things worth knowing: 1. The session is authorized with the Tilopay dashboard email and password, plus the verification code. The assistant never sees or stores that password: the connection runs on OAuth 2.0. 2. Because the user is tied to one merchant, the agent only reaches the data and the operations of that merchant. 3. You never paste your `apiKey`, `apiUser` or `apiPassword` into the MCP client. Your API credentials live encrypted on the Tilopay server and are decrypted on every call. They are not pasted into the MCP client, nor into the assistant configuration file, and they are never handed to the model. That is the security argument behind the whole design. ## Connection [#conexion] The server is `https://mcp.tilopay.com/mcp`. To connect: 1. Add the server in the MCP client with that URL. 2. The client discovers authentication from the 401 with `WWW-Authenticate`, which points to `/.well-known/oauth-protected-resource`. 3. The client registers dynamically and opens the authorization screen. 4. The user signs in with their Tilopay account credentials, confirms the verification code and sees a consent screen with the application requesting access, the return URL and the permissions. 5. On approval, the client is connected. The model is OAuth 2.0 with `authorization_code` and `refresh_token`. The client discovers the identity provider endpoints on its own: the only thing to configure is the server URL. ## From the most used MCP clients [#clientes] - **Claude** (desktop and web): add a remote connector with the server URL and complete sign-in in the window it opens. Connect with Claude - **ChatGPT**: add the server as a remote connector with that same URL; authorization completes in the browser. - **Cursor, VS Code and other editors**: declare a remote HTTP MCP server with the URL, with no token in the configuration file; the editor opens the browser to authorize. - **Your own client**: use an MCP client with Streamable HTTP transport and OAuth 2.0 support with dynamic registration. The sequence is the one above: 401, discovery, registration, authorization, connection. Want to start with ready-made cases? [Install a skill](/en/developers/agentes/mcp/skills). The transport is Streamable HTTP. The available tools are listed by group: [sales and transactions](/en/developers/agentes/mcp/ventas), [payment links](/en/developers/agentes/mcp/enlaces-de-pago), [catalog](/en/developers/agentes/mcp/catalogo), [contacts](/en/developers/agentes/mcp/contactos), [recurring billing](/en/developers/agentes/mcp/recurrentes), [support and diagnostics](/en/developers/agentes/mcp/soporte). The [banking API (BaaS)](/en/developers/agentes/mcp/api-bancario) group is enabled on request and only works for users with Tilopay banking API credentials. Before connecting an agent, read [what it can do](/en/developers/agentes/mcp/permisos). --- # Connect your AI agent > How to connect Claude, ChatGPT, GitHub Copilot, Cursor, Gemini or any other MCP client to the Tilopay MCP server. - kind: guide - status: stable - last_verified: 2026-09-23 - url: https://tilopay.com/en/developers/agentes/mcp/conectar Every agent connects to the same server through the same flow: you add a remote MCP server with the Tilopay URL and authorize the session in the browser. The only thing that changes per agent is where you add it. ```text https://mcp.tilopay.com/mcp ``` The MCP server version in production is **0.3.0**. ## Before connecting [#antes] 1. There is no form and no prior approval: an active Tilopay account is enough. 2. During authorization you sign in with your Tilopay dashboard email and password and confirm the verification code Tilopay sends. The assistant user is created automatically at that moment and tied to your account. 3. Review [what an agent can do](/en/developers/agentes/mcp/permisos) before connecting it, especially the tools marked as sensitive. Never paste your `apiKey`, `apiUser` or `apiPassword` into the agent or its config file. Merchant credentials live encrypted on the Tilopay server: the agent only handles its own OAuth session. ## Claude [#claude] Claude supports remote connectors, both in the desktop app and on the web. 1. Add a remote custom connector with the server URL. 2. Claude opens the Tilopay authorization screen: sign in with your Tilopay credentials and confirm the verification code. 3. Review the consent screen (requesting application, return URL and permissions) and approve. 4. Back in the conversation, the Tilopay tools are available. There is no token to paste: the connector registers itself. ## ChatGPT [#chatgpt] ChatGPT connects as a remote connector using the same URL. 1. Add the server as a remote connector with the URL above. 2. Authorization completes in the browser with your Tilopay credentials and the verification code. 3. Once approved, the connector is active and the model can call the tools. ## GitHub Copilot and VS Code [#copilot] In VS Code (with Copilot in agent mode) you declare a remote HTTP MCP server. 1. Declare the server with the URL, no token or credentials in the config file. 2. On first use, the editor opens the browser to authorize. 3. After approving, the tools show up in the agent's tool list. ## Cursor and other editors [#cursor] Cursor and equivalent editors use the same scheme: a remote MCP server over HTTP. 1. Add the remote server with the URL, no token in the configuration. 2. Authorize in the browser when the editor asks. 3. Enable the tools for the editor's chat or agent. ## Gemini [#gemini] Gemini clients that support remote MCP connect the same way: register the server URL and authorize in the browser. If the client only supports local stdio MCP servers, you need a bridge that speaks Streamable HTTP against the Tilopay URL. ## Other MCP clients [#otros] For a custom client or any other agent, the requirement is support for **Streamable HTTP** transport and **OAuth 2.0 with dynamic client registration**. The sequence is: 1. The client calls the server and gets a `401` with `WWW-Authenticate`, pointing to `/.well-known/oauth-protected-resource`. 2. The client discovers the identity provider endpoints. 3. It registers dynamically and opens the authorization screen. 4. The user signs in and approves the consent screen. 5. The client stays connected with `authorization_code` and `refresh_token`. The only thing you configure is the server URL. ## Verify the connection [#verificar] Ask the agent to list the available tools: the Tilopay ones should appear, grouped into [sales and transactions](/en/developers/agentes/mcp/ventas), [payment links](/en/developers/agentes/mcp/enlaces-de-pago), [catalog](/en/developers/agentes/mcp/catalogo), [contacts](/en/developers/agentes/mcp/contactos), [recurring billing](/en/developers/agentes/mcp/recurrentes), [support and diagnostics](/en/developers/agentes/mcp/soporte). The [banking API (BaaS)](/en/developers/agentes/mcp/api-bancario) group is enabled on request and only works for users with Tilopay banking API credentials. A safe first test is a read-only query, for example asking for transactions in a date range. For full flows see the [recipes](/en/developers/agentes/mcp/recetas). ## If something fails [#problemas] - **The client never opens authorization**: check the server is declared as remote over HTTP, not as a local server. - **Authorization never completes**: sign in with your Tilopay dashboard email and password, and confirm the verification code sent to your email. - **The agent sees no tools**: several clients require enabling them for the conversation or for agent mode after connecting. - **A tool returns empty**: it may be the queried environment; several tools default to `production`. --- # What an agent can do > The real scope of an agent connected to the MCP server: which tools read, which tools change state, and which controls exist. - kind: guide - status: stable - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/permisos ## Scope of the tools [#alcance] Of the 117 tools, 63 only read and 54 change state. 20 of those 54 are marked as sensitive. The ones marked as sensitive, grouped by theme. The list comes from the catalog: it is every tool with the sensitive access level. Sending messages (links, receipts or notices over WhatsApp or email) counts as a write, not as a sensitive operation. **Move money: refunds and captures, stored-card charges, bank transfers, their confirmation and dual control** - [`tilopay_modify_transaction`](https://tilopay.com/en/developers/agentes/mcp/ventas#tilopay-modify-transaction): Capture, refund or reverse - [`tilopay_charge_saved_card`](https://tilopay.com/en/developers/agentes/mcp/tarjetas-guardadas#tilopay-charge-saved-card): Charge a customer's saved card - [`tilopay_saved_cards_create_payments`](https://tilopay.com/en/developers/agentes/mcp/tarjetas-guardadas#tilopay-saved-cards-create-payments): Charge stored cards - [`baas_send_transfer`](https://tilopay.com/en/developers/agentes/mcp/api-bancario/send-transfer#baas-send-transfer): Send BaaS transfer - [`baas_confirm_transfer`](https://tilopay.com/en/developers/agentes/mcp/api-bancario/otras-operaciones#baas-confirm-transfer): Authorize the pending transfer - [`banca_decide_approval`](https://tilopay.com/en/developers/agentes/mcp/api-bancario/otras-operaciones#banca-decide-approval): Approve or reject a pending operation **Delete or deactivate: links, products, plans, coupons, contacts, stored cards, reminders, stores, domains and orders** - [`tilopay_delete_payment_link`](https://tilopay.com/en/developers/agentes/mcp/enlaces-de-pago#tilopay-delete-payment-link): Delete payment link - [`tilopay_catalog_delete_item`](https://tilopay.com/en/developers/agentes/mcp/catalogo#tilopay-catalog-delete-item): Delete a catalog product - [`tilopay_delete_contact`](https://tilopay.com/en/developers/agentes/mcp/contactos#tilopay-delete-contact): Delete contact - [`tilopay_store_delete`](https://tilopay.com/en/developers/agentes/mcp/microtienda#tilopay-store-delete): Disable a micro-store - [`tilopay_store_domain_remove`](https://tilopay.com/en/developers/agentes/mcp/microtienda#tilopay-store-domain-remove): Remove a custom domain - [`tilopay_orders_disable`](https://tilopay.com/en/developers/agentes/mcp/microtienda#tilopay-orders-disable): Turn off order management - [`tilopay_cancel_reminder`](https://tilopay.com/en/developers/agentes/mcp/recordatorios#tilopay-cancel-reminder): Cancel reminder - [`tilopay_recurring_delete_plan`](https://tilopay.com/en/developers/agentes/mcp/recurrentes#tilopay-recurring-delete-plan): Delete subscription plan - [`tilopay_recurring_delete_coupon`](https://tilopay.com/en/developers/agentes/mcp/recurrentes#tilopay-recurring-delete-coupon): Delete a coupon - [`tilopay_remove_saved_card`](https://tilopay.com/en/developers/agentes/mcp/tarjetas-guardadas#tilopay-remove-saved-card): Delete a customer's saved card **Change a subscriber's status and their future charges** - [`tilopay_recurring_manage_subscriber`](https://tilopay.com/en/developers/agentes/mcp/recurrentes#tilopay-recurring-manage-subscriber): Pause, reactivate or delete a subscriber - [`tilopay_recurring_edit_subscriber`](https://tilopay.com/en/developers/agentes/mcp/recurrentes#tilopay-recurring-edit-subscriber): Edit a plan's subscriber **Cut access: unlinking the account and removing an employee** - [`tilopay_unlink_account`](https://tilopay.com/en/developers/agentes/mcp/cuenta#tilopay-unlink-account): Unlink my account - [`tilopay_remove_employee`](https://tilopay.com/en/developers/agentes/mcp/cuenta#tilopay-remove-employee): Remove an employee's access ## Permission inheritance [#herencia-de-permisos] The server uses your API credentials, so it inherits exactly its permissions: it does not impose additional barriers per tool. An active user can issue refunds and run bulk charges. ## Controls that do exist [#controles] - A per-user activation switch, which cuts access without deleting the account. - A separate administrator role, which only serves to manage users and credentials. - A rate limit of 60 calls per minute per user. ## Human confirmation [#confirmacion-humana] Human confirmation before a sensitive operation depends on the MCP client, not on Tilopay. Always require it for write tools, and do not negotiate it for refunds or bulk charges. The per-tool detail, with parameters and output, lives on each group page and in the [`mcp.json`](/developers/mcp.json). --- # Recipes > Fourteen end-to-end flows with the MCP server: insights dashboard, selling over WhatsApp, recovering failed recurring charges, B2B collections, cash projection with banking data, anomaly detection, month-end close, refunds, recurring subscriptions, plan optimisation, stored-card charges, integration debugging and Tilopay as a tool for other agents. - kind: guide - status: stable - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/recetas ## Build a real-time insights dashboard [#dashboard-insights] 1. `tilopay_sales_summary` brings totals, costs and net for the period you want to monitor. 2. `tilopay_analyze_sales` turns those numbers into a narrative summary with trends and observations. 3. `tilopay_list_transactions` lists recent transactions to complete the board. 4. `tilopay_get_transaction` opens the detail of any operation that needs review. The summary uses UTC, not local time. If you have low volume, trends and top customers may come back empty. ### Example dashboard questions [#ejemplos-dashboard] - “How did I do today?” → `tilopay_sales_summary` + `tilopay_analyze_sales`. - “What are my best-selling products or services this week?” → `tilopay_analyze_sales` over the last 7 days. - “Do I have failed or pending transactions?” → `tilopay_list_transactions` filtered by status, then `tilopay_get_transaction` for each case. - “How are my net earnings evolving versus last month?” → compare two `tilopay_sales_summary` calls with different date windows. ## Sell over WhatsApp from the catalogue [#cobrar-por-whatsapp] 1. `tilopay_catalog_list_items` lists your products or services so the agent answers the customer with real names and prices. 2. `tilopay_catalog_get_item` opens the chosen item when price, currency or description must be confirmed before building the order. 3. The agent builds the order, computes the total and confirms it with the customer before charging. 4. `tilopay_save_contact` stores the buyer (name, email, phone) to attach them to the charge and reuse them in future sales. 5. `tilopay_create_charge` creates the link for the order total, with a description that identifies the order. 6. `tilopay_send_payment_link_whatsapp` prepares the message to the customer. 7. `tilopay_get_payment_link` checks the link status to confirm payment and close the conversation with the receipt. `tilopay_send_payment_link_whatsapp` does not send the message: it returns a `wa.me` link with the text ready to send in one tap. If there is no phone and no saved contact with that name, the tool asks for it. The agent can keep polling `tilopay_get_payment_link` to notify you as soon as the link is paid. With this recipe a merchant without an online store sells end to end inside the chat: catalogue, order, charge and confirmation, with no human intervention beyond what you choose to require. ## Recover failed recurring charges (smart dunning) [#dunning-inteligente] 1. `tilopay_recurring_list_plans` lists your active plans. 2. `tilopay_recurring_subscriber_payments` walks the payments of each plan's subscribers and separates the period's declined charges. 3. `tilopay_get_transaction` opens each decline to read the response code and classify the cause: insufficient funds, expired card, blocked card, suspected fraud. 4. Based on the cause, the agent decides the action and proposes it to you: wait for the plan's retry when the decline is transient (insufficient funds), or ask the customer for a new payment method when it is final (expired or blocked card). 5. `tilopay_create_charge` creates a link for the outstanding amount when the customer must pay the instalment with another card. 6. `tilopay_send_payment_link_whatsapp` or `tilopay_send_payment_link_email` prepares the message to the customer with wording adapted to the decline reason. 7. `tilopay_get_payment_link` confirms when the customer paid, and `tilopay_recurring_get_subscriber` verifies the subscriber's status after recovery. An agent running this recipe every morning turns the decline list into two queues: the one that resolves itself with the plan's retry and the one that needs a message to the customer. You only receives the summary: how many charges were recovered and which remain pending. Do not use `tilopay_recurring_manage_subscriber` to "punish" a decline: `pause` and `delete` affect future charges and `delete` cannot be undone. Pausing or removing a subscriber is a merchant decision with explicit human confirmation, never an automatic reaction to a failed charge. ## B2B collections with accounts receivable [#cobranza-b2b] 1. `tilopay_list_contacts` brings your business customers; `tilopay_save_contact` registers the missing ones with the payer's name, email and phone. 2. `tilopay_create_charge` creates a link per outstanding invoice, with the invoice number in the description and a due date. 3. `tilopay_send_payment_link_email` prepares the first send; `tilopay_send_payment_link_whatsapp` prepares the reminder when the invoice is still unpaid. 4. `tilopay_get_payment_link` checks each link's status to update the receivables list without opening the panel. 5. `baas_search_payment` or `baas_list_payments` look in your bank accounts for a direct transfer (SINPE or bank transfer) matching the amount and customer, to mark as paid an invoice the customer settled outside the link. 6. `tilopay_delete_payment_link` removes the link of an invoice already paid by transfer, to avoid a double charge. This recipe answers the "have they paid me yet?" that eats administrative hours in businesses that invoice companies: one list with invoices, links, status and payments received through any channel. The `baas_*` tools are read-only and require support to have registered your banking API credentials. If they are not configured, the tool says so and the recipe keeps working with payment links alone. ## Project cash with gateway and bank data (pocket CFO) [#cfo-de-bolsillo] 1. `tilopay_sales_summary` brings the last weeks of sales to estimate expected income for the next period. 2. `tilopay_recurring_list_plans` and `tilopay_recurring_get_plan` quantify committed recurring charges and their dates. 3. `baas_list_balances` or `baas_get_balance` bring the real balance of each of your bank accounts. 4. `baas_list_payments` lists the last months of transfers to detect recurring outflows: payroll, rent, suppliers. 5. `baas_analyze_payments` summarises those movements in natural language to identify patterns and peak dates. 6. The agent combines balance, expected inflows and expected outflows into a daily projection, flags the days with minimum or negative balance and proposes actions. 7. `tilopay_create_charge` and `tilopay_send_payment_link_email` put outstanding invoices out for collection when you approve that action. The value is in crossing two sources that normally live apart: the gateway knows what is coming in and the bank knows what is there and what goes out. A weekly routine produces a short briefing with the projection and the alerts, before the business opens. The `baas_*` tools never initiate, approve or reverse transfers. If the projection suggests moving funds between accounts, a person executes that at the bank; the agent only recommends and records it. The projection is an estimate, not a financial statement. ## Detect anomalies and act [#detector-anomalias] 1. `tilopay_sales_summary` compares the current window (for example the last hour or the day) against the same reference period to detect approval drops, decline spikes or sudden changes in average ticket. 2. `tilopay_list_transactions` filters by status to see whether declines concentrate on one payment method, one currency or a pattern of repeated attempts. 3. `tilopay_get_transaction` opens representative cases to read the response codes. 4. `tilopay_diagnostics` checks credentials and endpoint availability to separate a problem in the merchant's integration from a problem with customers or issuers. 5. `tilopay_help_guides` searches the official guides for the explanation and the fix that match the response code or the symptom. 6. The agent raises the alert with the diagnosis already done: what changed, since when, probable cause and suggested action. The sales summary uses UTC. When comparing short windows, align the hours; a "drop" at local midnight may just be the UTC day cut. Many declined attempts in a few minutes with small amounts and different cards may indicate stolen-card testing: report it to support instead of retrying. ## Close the month sales [#cierre-de-mes] 1. `tilopay_sales_summary` returns totals, costs and net for the period. 2. `tilopay_analyze_sales` produces the natural-language report. 3. `tilopay_get_transaction` brings the detail of the transactions that need review. The hours and days in the summary are in UTC, not local time. Trends and top customers come back empty when the sample is small. ## Refund with human confirmation [#reembolso] 1. `tilopay_list_transactions` locates the transaction in the date range. 2. `tilopay_get_transaction` shows the detail to confirm it is the right one. 3. The agent asks the user for confirmation. That step is enforced by the MCP client, not by Tilopay. 4. `tilopay_modify_transaction` runs the modification with the action and the amount. ## Review a recurring subscription [#suscripcion-recurrente] 1. `tilopay_recurring_list_plans` lists you recurring billing plans. 2. `tilopay_recurring_get_plan` opens the plan you care about by its ID. 3. `tilopay_recurring_get_subscriber` shows the subscriber detail. 4. `tilopay_recurring_subscriber_payments` lists that subscriber payments to see which went through and which did not. Useful to answer "why does this customer show no charge this month?" without opening the dashboard. ## Optimise subscription plans [#optimizar-planes] 1. `tilopay_recurring_list_plans` lists the active plans with price and frequency. 2. `tilopay_recurring_get_plan` opens each plan for its subscriber count and configuration. 3. `tilopay_recurring_subscriber_payments` walks payments per subscriber to compute, per plan, the successful-charge rate, the month when most subscribers stop paying and the average tenure. 4. `tilopay_recurring_get_subscriber` reviews the outliers the analysis flags. 5. The agent presents the comparison between plans: retention, revenue per subscriber and churn point, and proposes changes to price, frequency or content. 6. `tilopay_create_charge` lets you test a new offer with a small segment of contacts before modifying a plan. This is pricing analysis with real charge data. The agent does not modify plans: it proposes, and you decide in the panel. ## Pause or cancel a subscriber [#pausar-suscriptor] 1. `tilopay_recurring_get_subscriber` confirms the subscriber is the right one. 2. The agent asks the user for confirmation, with the name and the plan in plain sight. 3. `tilopay_recurring_manage_subscriber` runs `pause`, `reactivate` or `delete`. `tilopay_recurring_manage_subscriber` is marked sensitive: it affects future charges and `delete` cannot be undone. Never run it without explicit human confirmation. ## Charge a group with stored cards [#cobro-masivo] 1. `tilopay_saved_cards_list_groups` lists the collection groups. 2. `tilopay_saved_cards_list_affiliates` shows the affiliates of the chosen group, so you know how many cards will be charged. 3. The agent summarises amount, currency, reason and number of affiliates, and asks for confirmation. 4. `tilopay_saved_cards_create_payments` creates the charges. 5. `tilopay_saved_cards_list_collections` and `tilopay_saved_cards_collection_detail` review the result: the second one needs the `code` returned by the first. `tilopay_saved_cards_create_payments` moves real money from the stored cards. Treat it as a one-shot operation: confirm before, and verify after with the collection detail. ## Debug an integration and support a developer [#depurar-integracion] 1. `tilopay_diagnostics` tests the credentials and one endpoint per API group, to separate a credentials problem from a request problem. 2. `tilopay_debug_code` receives the code snippet, log or stack trace and returns diagnosis, root cause, fix and risks. 3. `tilopay_help_guides` answers panel usage questions with the official guides. 4. `tilopay_list_transactions` and `tilopay_get_transaction` confirm whether the operation the developer believes failed actually reached Tilopay, and with what result. `tilopay_debug_code` does not read the repository or run the code: it analyses only what you pass in. Strip credentials from the snippet before sending; the tool masks what it recognises as a secret, but that does not replace your review. With these four tools a support agent handles the whole technical onboarding: verifies the account, finds the cause of the error, delivers the fix with the official guide and confirms against real transactions. Repeated questions are the signal of which guide or example the portal still needs. Sandbox test cards are in the portal's testing guide. Sandbox mode is switched from the account; there is no separate host. ## Tilopay as a tool for other agents [#tilopay-como-herramienta] 1. Access is self-service: you add the MCP server in your client with the URL `https://mcp.tilopay.com/mcp` and authorize the session with your Tilopay account email and password, plus the verification code we send. There is no form and no prior approval. 2. The external agent (an ERP assistant, an online-store bot, an accounting agent) connects to the MCP server with that OAuth session and runs `tilopay_diagnostics` to verify the connection. 3. With the tool catalogue loaded, the external agent uses the read tools (`tilopay_sales_summary`, `tilopay_list_transactions`, `tilopay_recurring_*`, `baas_*`) to answer business questions inside its own conversation. 4. For actions that create or move money (`tilopay_create_charge`, `tilopay_modify_transaction`, `tilopay_saved_cards_create_payments`, `tilopay_recurring_manage_subscriber`) the external agent applies the confirmation policy described on the permissions page before executing. This recipe changes the MCP's position: it is not just an assistant inside Tilopay, it is the payments layer any agent can call. Every other recipe on this page works the same when a third-party agent runs it. MCP usage may carry additional costs depending on volume; support details them when validating the merchant's transaction volume. Tools marked as sensitive require human confirmation in whichever MCP client runs them, Tilopay's or a third party's. Want these recipes packaged and repeatable? [Install a skill](/en/developers/agentes/mcp/skills). --- # Skills for agents > Ready-made instruction packages so an agent can use the Tilopay MCP for concrete tasks, such as the cash close, charging over WhatsApp or recovering declined payments. - kind: guide - status: stable - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/skills ## What a skill is [#que-es] A skill is a package of instructions (a `SKILL.md` plus its references) that the agent loads only when you ask for something on that topic. It requires no code and no credentials: it uses the Tilopay MCP that is already connected, and the instructions tell the agent which tools to call, in what order and how to validate the numbers before answering. The difference from [recipes](/en/developers/agentes/mcp/recetas) is that a recipe is the example prompt; a skill packages it so it is repeatable, with calculation and safety rules. ## Before you start [#antes] You need a Tilopay account. If you install the plugin (option 2 below), the MCP connector is included and you only have to authorize it with your account the first time. For the other options, first [connect the MCP in your client](/en/developers/agentes/mcp/conectar). Skills never ask for or store credentials. ## Available skills [#skills] ### All skills in one plugin All six skills and the Tilopay MCP connector in one plugin for Cowork, the claude.ai chat and Claude Code. Install it from the Tilopay marketplace with the commands below. ```text /plugin marketplace add tilopay/claude-plugins /plugin install tilopay-mcp-skills@tilopay ``` - [View on GitHub](https://github.com/tilopay/claude-plugins) - Version: 1.0.1 - Copy prompt for Claude Code: “Read the Tilopay skills index at https://tilopay.com/en/developers/skills/index.md. For each skill, create a folder with its name inside ~/.claude/skills/, save its SKILL.md there and, if the index lists reference files, save them in a references/ subfolder with the same file name. When you finish, tell me which skills were installed.” ### cierre-de-caja Cash close for the day, week or month by currency: approved, declined, refunds, exact breakdown of commissions and withholdings, net to settle and next deposit, validating that the numbers match Tilopay's official balance. - Example prompt: «Give me today's cash close» - MCP tools: `tilopay_list_transactions`, `tilopay_sales_summary`, `tilopay_settlement_balance`, `tilopay_settlements_list`, `tilopay_switch_commerce` - Groups: [Sales and transactions](https://tilopay.com/en/developers/agentes/mcp/ventas) - Version: 1.0.1 - [SKILL.md](https://tilopay.com/developers/skills/cierre-de-caja/SKILL.md) - Prompt to use it without installing: “Read https://tilopay.com/developers/skills/cierre-de-caja/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. Give me today's cash close” ### cobrar-por-whatsapp Turns a sentence into a real charge and a WhatsApp message ready to send; generates the QR code when asked and later checks whether the customer has paid. - Example prompt: «Charge Ana 25,000 colones for the photography course» - MCP tools: `tilopay_create_charge`, `tilopay_send_payment_link_whatsapp`, `tilopay_send_payment_link_email`, `tilopay_payment_link_qr`, `tilopay_list_receipts`, `tilopay_find_payment`, `tilopay_list_contacts` - Groups: [Payment links](https://tilopay.com/en/developers/agentes/mcp/enlaces-de-pago), [Receipts](https://tilopay.com/en/developers/agentes/mcp/comprobantes), [Contacts](https://tilopay.com/en/developers/agentes/mcp/contactos) - Version: 1.0.1 - [SKILL.md](https://tilopay.com/developers/skills/cobrar-por-whatsapp/SKILL.md) - Prompt to use it without installing: “Read https://tilopay.com/developers/skills/cobrar-por-whatsapp/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. Charge Ana 25,000 colones for the photography course” ### rechazos-y-recuperacion Analyzes declined payments, groups them by reason using the labels of the official “Decline reasons” guide, detects patterns (same customer retrying, same order recovered later, channel, time of day) and proposes concrete actions, including the message to the customer and a new link. - Example prompt: «Why were payments declined this month and which ones can I recover?» - MCP tools: `tilopay_list_transactions`, `tilopay_sales_summary`, `tilopay_create_charge`, `tilopay_send_payment_link_whatsapp`, `tilopay_help_guides` - Groups: [Sales and transactions](https://tilopay.com/en/developers/agentes/mcp/ventas), [Payment links](https://tilopay.com/en/developers/agentes/mcp/enlaces-de-pago), [Support and diagnostics](https://tilopay.com/en/developers/agentes/mcp/soporte) - Version: 1.0.1 - [SKILL.md](https://tilopay.com/developers/skills/rechazos-y-recuperacion/SKILL.md) - Prompt to use it without installing: “Read https://tilopay.com/developers/skills/rechazos-y-recuperacion/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. Why were payments declined this month and which ones can I recover?” - [“Decline reasons” guide (Spanish)](https://tilopay.com/guias/panel-transacciones/motivos-de-rechazo) ### conciliacion-mensual Accounting reconciliation for a month, half-month or date range: cross-checks approved sales, refunds and deposited settlements, with all six cost fields labeled, and delivers a CSV or Excel file for the accountant plus a summary of discrepancies. - Example prompt: «Reconcile September for my accountant» - MCP tools: `tilopay_list_transactions`, `tilopay_refunds_list`, `tilopay_settlements_list`, `tilopay_settlement_transactions`, `tilopay_settlement_balance`, `tilopay_billing_list`, `tilopay_switch_commerce` - Groups: [Sales and transactions](https://tilopay.com/en/developers/agentes/mcp/ventas), [Settlements, deposits and billing](https://tilopay.com/en/developers/agentes/mcp/liquidaciones), [Account, commerce and team](https://tilopay.com/en/developers/agentes/mcp/cuenta) - Version: 1.0.0 - [SKILL.md](https://tilopay.com/developers/skills/conciliacion-mensual/SKILL.md) - Prompt to use it without installing: “Read https://tilopay.com/developers/skills/conciliacion-mensual/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. Reconcile September for my accountant” ### cfo-de-bolsillo Financial analysis of sales: compares periods of equal length, average transaction value, best days and hours, customer concentration, Tilopay's effective cost as a percentage of sales, and a simple projection of month-end results and cash flow with the next deposit, always labeled as an estimate. - Example prompt: «How am I doing this month compared with last month?» - MCP tools: `tilopay_list_transactions`, `tilopay_sales_summary`, `tilopay_settlement_balance`, `tilopay_settlements_list`, `tilopay_get_business_profile` - Groups: [Sales and transactions](https://tilopay.com/en/developers/agentes/mcp/ventas), [Settlements, deposits and billing](https://tilopay.com/en/developers/agentes/mcp/liquidaciones), [Business profile](https://tilopay.com/en/developers/agentes/mcp/perfil) - Version: 1.0.0 - [SKILL.md](https://tilopay.com/developers/skills/cfo-de-bolsillo/SKILL.md) - Prompt to use it without installing: “Read https://tilopay.com/developers/skills/cfo-de-bolsillo/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. How am I doing this month compared with last month?” ### catalogo-y-cobros-fijos Manages the product and service catalog: finds products with their payment links, creates or edits them from a sentence or a list, provides a product QR code or a permanent countertop QR code, and connects the catalog to Facebook/Instagram, Google Shopping, TikTok or Pinterest. - Example prompt: «Add Yoga class to the catalog for 8,000 colones» - MCP tools: `tilopay_catalog_list_items`, `tilopay_catalog_get_item`, `tilopay_catalog_create_item`, `tilopay_catalog_update_item`, `tilopay_catalog_delete_item`, `tilopay_payment_link_qr`, `tilopay_static_payment_qr`, `tilopay_catalog_feed`, `tilopay_store_list`, `tilopay_send_payment_link_whatsapp` - Groups: [Catalog](https://tilopay.com/en/developers/agentes/mcp/catalogo), [Payment links](https://tilopay.com/en/developers/agentes/mcp/enlaces-de-pago), [Microstore](https://tilopay.com/en/developers/agentes/mcp/microtienda) - Version: 1.0.0 - [SKILL.md](https://tilopay.com/developers/skills/catalogo-y-cobros-fijos/SKILL.md) - Prompt to use it without installing: “Read https://tilopay.com/developers/skills/catalogo-y-cobros-fijos/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. Add Yoga class to the catalog for 8,000 colones” ## How to install [#instalar] **Option 1 · Use it right now, without installing anything (Claude, ChatGPT or another assistant with the MCP connected).** 1. Open a new conversation with the Tilopay MCP connected. 2. On the skill's card, tap "Copy prompt". 3. Paste it and send it. The assistant reads the instructions from tilopay.com and applies them in that conversation. Next time, paste the prompt again (or use option 2 to keep it set up). For example, the cash close prompt: ```text Read https://tilopay.com/developers/skills/cierre-de-caja/SKILL.md and the files it references, and follow those instructions using my Tilopay connector. Give me today's cash close ``` **Option 2 · Install all six at once in Claude (recommended).** Tilopay publishes its skills as a plugin on GitHub. Add it once and it stays saved in your account: it works in Cowork, in the claude.ai chat and in Claude Code. The plugin includes the Tilopay MCP connector: the first time you use it, Claude asks you to authorize it with your Tilopay account (in Cowork, from the plugin's Connectors tab). In the Claude app (Cowork): 1. Open the Cowork tab, then Customize in the sidebar. 2. Go to Plugins and click Add → Add marketplace → Add from a repository. 3. Enter `tilopay/claude-plugins` and confirm. 4. Go back to Discover, choose tilopay-mcp-skills and click Install. In Claude Code (terminal), run these two commands: ```text /plugin marketplace add tilopay/claude-plugins /plugin install tilopay-mcp-skills@tilopay ``` From then on, when you ask for something on that topic (“give me today's cash close”) with the Tilopay MCP connected, Claude loads the skill automatically. To get improvements, use Check for updates in the marketplace or turn on Sync automatically (in Claude Code: `/plugin marketplace update tilopay`).

Plugins are available on Claude's paid plans. If your organization limits which marketplaces you can add, an Owner must allow tilopay/claude-plugins or sync it for everyone from the organization settings.

**Option 3 · Keep one skill set up in a Claude Project.** The simplest way without installing anything: create a Project in Claude, open the skill's `SKILL.md` (the link on the card), copy all its content and paste it into the project instructions. For the assistant to use it in any conversation, paste the option 1 prompt whenever you need it. **Option 4 · Set it up by hand in Claude Code (without the marketplace).** Paste this prompt into Claude Code: ```text Read the Tilopay skills index at https://tilopay.com/en/developers/skills/index.md. For each skill, create a folder with its name inside ~/.claude/skills/, save its SKILL.md there and, if the index lists reference files, save them in a references/ subfolder with the same file name. When you finish, tell me which skills were installed. ``` The prompt asks Claude Code to create one folder per skill under `~/.claude/skills/` (or in the project's `.claude/skills/` to share it with your team), saving each skill's published `SKILL.md` and, when it has reference files, those files too in a `references/` subfolder. **Other MCP clients.** Use the `SKILL.md` as system or project instructions, or paste the prompt from option 1. ## How it works inside [#como-funciona] Taking the cash close as an example, the sequence is: 1. The agent converts "today" to your local time. 2. It calls `tilopay_list_transactions` with that range expressed in UTC. 3. It classifies each row only by `code`: `1` is approved and `1101` is a refund. 4. It adds up the six cost fields by their exact names: `commission`, `iva_commission`, `rent`, `retention_iva`, `cost` and `iva_tilopay`. 5. It validates that gross − deductions = net and cross-checks it with `tilopay_settlement_balance` before answering. Skills never call tools that move money without your confirmation. ## Create your own skill [#crear] The minimum structure of a `SKILL.md` is a frontmatter with `name` and `description`, followed by the procedure: ```markdown --- name: my-skill description: What the skill does and when the agent should use it. --- 1. Procedure step, with the exact tool being called. 2. Validation rule before answering. ``` Three tips: 1. Name the exact tools the agent must call. 2. Include the validation rules for the numbers. 3. Say what NOT to do. To see every available tool, check the manifest [`/developers/mcp.json`](/developers/mcp.json). If you built a skill that is useful for other merchants, send it to [soporte@tilopay.com](mailto:soporte@tilopay.com). --- # Acquiring tools > Summary of the MCP tool groups over the acquiring API: sales, payment links, catalog, contacts, business profile, micro-store and orders, alerts, receipts, reminders, recurring charges, stored cards and support. - kind: mcp-index - status: stable - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/adquirencia ## What it covers [#que-cubre] These are the tools that work over Tilopay's **acquiring API**: card payments, payment links and the data around them. They come enabled with standard MCP access, with no extra request, and they always operate on the account tied to the MCP user. The [banking API](/developers/agentes/mcp/api-bancario) tools are a separate case: they are enabled on request and require banking API credentials. ## Groups [#grupos] - [Sales and transactions](https://tilopay.com/en/developers/agentes/mcp/ventas) — 8 tools: Transaction lookups, sales metrics and modifying a transaction (capture, refund or reversal). - [Payment links](https://tilopay.com/en/developers/agentes/mcp/enlaces-de-pago) — 7 tools: Creating, reading and deleting payment links, plus preparing delivery over WhatsApp or email. - [Catalog](https://tilopay.com/en/developers/agentes/mcp/catalogo) — 6 tools: Reading the catalog of products and services the merchant already created as payment links. - [Contacts](https://tilopay.com/en/developers/agentes/mcp/contactos) — 3 tools: The merchant address book: saved contacts to reuse phone and email when sending a payment link. - [Business profile and recipes](https://tilopay.com/en/developers/agentes/mcp/perfil) — 3 tools: The merchant business profile (what they do, what they sell, how they charge and what they want to improve) and the automation recipes the agent suggests from it. - [Micro-store and orders](https://tilopay.com/en/developers/agentes/mcp/microtienda) — 13 tools: Micro-stores with a cart built from the catalog, plus managing the orders they generate: setup, summary, listing, pending deliveries and closing. - [Alerts and notifications](https://tilopay.com/en/developers/agentes/mcp/avisos) — 2 tools: The merchant WhatsApp alerts: approved and declined charges, daily summary and orders. These tools only read and change the settings; they do not send messages on their own. - [Payment receipts](https://tilopay.com/en/developers/agentes/mcp/comprobantes) — 2 tools: Receipts for approved charges: listing them and resending a customer the receipt for their payment. - [Reminders](https://tilopay.com/en/developers/agentes/mcp/recordatorios) — 3 tools: WhatsApp reminders, one-off or recurring, which can run an agent action when they fire (sales summary, pending links, balances). - [Recurring billing](https://tilopay.com/en/developers/agentes/mcp/recurrentes) — 23 tools: Subscription plans and their subscribers: reading, creating and editing plans, coupons, subscription links, failed charges and the guided step-by-step to build a plan. - [Stored cards and bulk collections](https://tilopay.com/en/developers/agentes/mcp/tarjetas-guardadas) — 10 tools: Customers' stored cards and bulk collections: tokenization, available payment methods, charging a stored card, removing a card and collection groups. - [Settlements, deposits and billing](https://tilopay.com/en/developers/agentes/mcp/liquidaciones) — 7 tools: - [Account, merchant and team](https://tilopay.com/en/developers/agentes/mcp/cuenta) — 8 tools: - [Support and diagnostics](https://tilopay.com/en/developers/agentes/mcp/soporte) — 3 tools: Answers taken from the official guides and a connectivity test against each API group. The access level of each tool and the detail of its parameters live on its group page; the permissions summary is in [Permissions](/developers/agentes/mcp/permisos). --- # Sales and transactions > MCP tools to query transactions, measure sales and modify a transaction. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/ventas ## What it covers [#que-cubre] This group covers reading transactions and analysing your sales, plus modifying a transaction: capture, refund or reversal. The modification moves real money and is marked as sensitive. ## Tools [#herramientas] ### Sales summary and trends [#tilopay-sales-summary] - `tilopay_sales_summary` - Access: Read only - API operation: `POST /api/v1/consultTransactions (y cálculo local)` — https://tilopay.com/en/developers/api/procesos-operativos/consult-transactions Computes sales metrics from the transactions: totals by currency, average ticket, approval rate, sales by day, weekday and hour, top customers, decline reasons and trend. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `startDate` | string | yes | Start date "YYYY-MM-DD HH:mm:ss" | | `endDate` | string | yes | End date "YYYY-MM-DD HH:mm:ss" | | `includeDeclined` | boolean | — | Include declined transactions to measure the approval rate (true by default) | | `environment` | string (production | test) | — | — | | `currency` | array | — | — | **Returns** `{ summary, trends, environmentNote }` summary carries range, timezoneNote, totalRows, payments {total, approved, declined, approvalRate}, refunds {total, approved, failed, successRate}, byCurrency per currency with payments, itemised costs (commission, iva_commission, cost, cost_iva, retention_iva, retention_rent, totalDeducted, taxWithholdings, pspCost), netToLiquidate, reconciles and reconciliationDelta; refunds; and netForPeriod. It also returns daily, sample, byWeekday, byHour, topCustomers, declineReasons, refundFailureReasons and transactionTypes. byWeekday, byHour and topCustomers come back null when the sample is too small (fewer than 30 rows or fewer than 5 distinct days). trends comes back null in that same case. Hours and days are in UTC. ### Sales analyst agent [#tilopay-analyze-sales] - `tilopay_analyze_sales` - Access: Read only - API operation: `POST /api/v1/consultTransactions (y análisis con modelo)` — https://tilopay.com/en/developers/api/procesos-operativos/consult-transactions Agent that analyses the transactions in a date range and returns a natural-language report: performance, trends, seasonality, approval quality, risks and actionable recommendations. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `startDate` | string | yes | Start date "YYYY-MM-DD HH:mm:ss" | | `endDate` | string | yes | End date "YYYY-MM-DD HH:mm:ss" | | `question` | string | — | Specific question or focus for the analysis | | `environment` | string (production | test) | — | — | | `currency` | array | — | — | **Returns** `{ report, summary, trends, environmentNote }` report is the natural-language report; summary and trends are the same ones from tilopay_sales_summary. When there are no transactions in the range it returns only a text saying so, with no structuredContent. ### Did they pay me yet? [#tilopay-find-payment] - `tilopay_find_payment` - Access: Read only Checks whether a customer already paid: it reviews the Tilopay charges and, if the banking session is open, also the incoming entries of the bank account. Use it for questions like 'did Ana pay me yet?' or 'did the ₡25,000 payment come in?'. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | — | Nombre del cliente (coincidencia parcial) | | `amount` | number | — | Monto del pago esperado | | `currency` | string | — | Moneda del monto, ej. CRC o USD | | `hours` | integer | — | Horas hacia atrás a revisar (por defecto 24) | | `reference` | string | — | Número de orden o referencia | **Returns** ### List Tilopay transactions [#tilopay-list-transactions] - `tilopay_list_transactions` - Access: Read only - API operation: `POST /api/v1/consultTransactions` — https://tilopay.com/en/developers/api/procesos-operativos/consult-transactions Queries Tilopay transactions in a date range. It can filter by currency, order number, customer email and environment. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `startDate` | string | yes | Start date, e.g. "2026-08-01 00:00:00" | | `endDate` | string | yes | End date, e.g. "2026-08-31 23:59:59" | | `status` | string (approved | declined | all) | — | approved = only approved, declined = only declined, all = all | | `sort` | string (newest | oldest) | — | newest = most recent first | | `onlyAproved` | boolean | — | Compatibility: false is equivalent to status: "all" | | `environment` | string (production | test) | — | Environment, production by default | | `currency` | array | — | Currencies, e.g. ["USD","CRC"] | | `orderNumber` | string | — | Filter by order number | | `email` | string | — | Filter by customer email | | `limit` | integer | — | Maximum rows to return (100 by default, 500 maximum) | **Returns** `{ total, transactions[], environmentNote }` total = rows found before applying limit; transactions = the rows returned; environmentNote warns when the queried environment returned no rows. ### Get one transaction [#tilopay-get-transaction] - `tilopay_get_transaction` - Access: Read only - API operation: `POST /api/v1/consult` — https://tilopay.com/en/developers/api/procesos-operativos/consult Gets the detail of a specific Tilopay transaction from your order number. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `orderNumber` | string | yes | Order number of the transaction | | `merchantId` | string | — | Merchant ID (optional) | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Capture, refund or reverse [#tilopay-modify-transaction] - `tilopay_modify_transaction` - Access: Sensitive - API operation: `POST /api/v1/processModification` — https://tilopay.com/en/developers/api/procesos-operativos/process-modification Modifies a Tilopay transaction: capture, refund or reversal for the given amount. The amount can never exceed that of the original sale: if the user asks for more, tell them the real sale amount and ask whether to refund that amount or part of it; do not ask for confirmation of a larger amount. It only refunds approved sales, never an order that is already a refund (it starts with «Re-»). When Tilopay confirms the refund, a text receipt ready to forward to the customer is generated. Sensitive operation: it affects real money and requires a second factor. Call it first without `confirmation_code`: a 6-digit code is sent to the merchant's email and the response includes `requires_confirmation`. Repeat the same call, with the same parameters, adding `confirmation_code`. Report the result exactly as the tool returns it (state): if it is pending or not confirmed, never say the refund was already made. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `orderNumber` | string | yes | Order number of the transaction | | `action` | string (capture | refund | reversal) | yes | Type of modification | | `amount` | number | yes | Amount to modify, greater than zero | | `currency` | string | — | Moneda del movimiento, ej. CRC o USD | | `client_name` | string | — | Nombre del cliente, para el comprobante | | `client_email` | string | — | Correo del cliente, para el comprobante | | `confirmation_code` | string | — | 6-digit code received by email to authorize the operation | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Refund history [#tilopay-refunds-list] - `tilopay_refunds_list` - Access: Read only The merchant's refunds. pending=true returns the refunds pending processing; otherwise, the history. Query only: to refund use tilopay_modify_transaction. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `pending` | boolean | — | Solo pendientes | | `page` | integer | — | Página (por defecto 1) | | `limit` | integer | — | Registros por página (por defecto 20) | | `search` | string | — | Texto a buscar | **Returns** ### Scheduled cash close [#tilopay-cash-close] - `tilopay_cash_close` - Access: Write Daily cash close by WhatsApp: the day's sales per currency, declines grouped by reason and the next Tilopay settlement. Action 'get' shows the configuration; 'set' changes it (enabled, time_of_day 'HH:mm' in local time, days 0=Sunday…6=Saturday; 'Monday to Saturday' = [1,2,3,4,5,6]); 'preview' builds today's close right now. For 'turn off the close' use set with enabled=false. Only the owner can configure it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `action` | string (get | set | preview) | yes | — | | `enabled` | boolean | — | — | | `time_of_day` | string | — | Hora local 'HH:mm' (24 h). 8 pm = '20:00' | | `days` | array | — | 0=domingo … 6=sábado | **Returns** --- # Payment links > MCP tools to create, read and delete payment links, and prepare their delivery. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/enlaces-de-pago ## What it covers [#que-cubre] With this group the agent creates a payment link, reads its detail, deletes it and prepares delivery to the customer over WhatsApp or email. WhatsApp delivery does not send the message: it returns a link ready to send. ## Tools [#herramientas] ### Create a charge with a link [#tilopay-create-charge] - `tilopay_create_charge` - Access: Write Creates a charge and returns the Tilopay short link (tilo.co/s/…) that opens the secure payment form directly; if it is not available, the own link tilopay.shop/pagar/…. The response includes url (the short one when it exists) and fallback_url (the own one); both work. On WhatsApp, send the QR as an image together with the link at once; do not call the QR tool afterwards. If customer data is missing, the link page asks for it before charging. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `amount` | number | yes | Monto a cobrar | | `currency` | string | — | Moneda, ej. USD o CRC. Si se omite se usa la del país del comercio. | | `reference` | string | yes | Referencia del cobro en texto normal, con espacios. No convertir a slug ni reemplazar espacios por guiones o guiones bajos. | | `description` | string | yes | Concepto visible para el cliente. Conserva literalmente el texto del comercio con espacios, acentos y mayúsculas; por ejemplo 'prueba link', nunca 'prueba_link'. | | `client` | string | — | Nombre del cliente | | `client_email` | string | — | Correo del cliente | | `client_phone` | string | — | Teléfono del cliente | **Returns** ### Payment link detail [#tilopay-get-payment-link] - `tilopay_get_payment_link` - Access: Read only - API operation: `GET /api/v1/getDetailLinkPayment/{link_payment_id}/{api_key}` — https://tilopay.com/en/developers/api/links-de-pago/detail-link-payment Returns the detail of a payment link by its ID. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `linkPaymentId` | string | yes | Payment link ID | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Delete payment link [#tilopay-delete-payment-link] - `tilopay_delete_payment_link` - Access: Sensitive - API operation: `POST /api/v1/deleteLinkPayment` — https://tilopay.com/en/developers/api/links-de-pago/delete-link-payment Deletes a payment link by its ID. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | ID of the payment link to delete | | `confirmation_code` | string | — | 6-digit code received by email to authorize the operation | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Send payment link over WhatsApp [#tilopay-send-payment-link-whatsapp] - `tilopay_send_payment_link_whatsapp` - Access: Write Prepares sending the payment link over WhatsApp: it returns a wa.me link with the message ready. It accepts the name of a saved contact or a phone number. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `link_url` | string | yes | Payment link URL | | `contact_name` | string | — | Name of the saved contact or of the customer | | `phone` | string | — | WhatsApp phone, e.g. +50688887777 | | `amount` | number | — | — | | `currency` | string | — | — | | `description` | string | — | — | | `message` | string | — | Your own message text | | `save_contact` | boolean | — | Save the contact in the address book | | `send_now` | boolean | — | Deprecated: ignored. The wa.me link is always returned. | **Returns** `{ to, message, whatsapp_url }` It does not send the message: it returns the resolved phone, the drafted text and a wa.me link to send it with one tap. When there is no phone and no saved contact with that name, it returns an error asking for it. It saves the contact unless save_contact is false. ### Send payment link over email [#tilopay-send-payment-link-email] - `tilopay_send_payment_link_email` - Access: Write Emails the payment link to the customer. It accepts the name of a saved contact or an email address. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `link_url` | string | yes | Payment link URL | | `email` | string | — | Recipient email | | `contact_name` | string | — | Name of the saved contact or of the customer | | `amount` | number | — | — | | `currency` | string | — | — | | `description` | string | — | — | | `message` | string | — | Additional note for the customer | | `save_contact` | boolean | — | Save the contact in the address book | **Returns** `{ sent, to }` sent is true and to is the address it was sent to. When there is no email and no saved contact with that name, it returns an error asking for it. ### QR of a payment link or product [#tilopay-payment-link-qr] - `tilopay_payment_link_qr` - Access: Read only Generates the QR of a payment link or of a catalog product and sends it as an image (PNG). Use it when the merchant asks for the QR to share or print. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `link_url` | string | — | Enlace de pago completo (https://...) | | `catalog_id` | string | — | ID del producto del catálogo | | `label` | string | — | Nombre para el pie de la imagen | **Returns** ### Permanent open-amount QR [#tilopay-static-payment-qr] - `tilopay_static_payment_qr` - Access: Read only Returns the merchant's permanent open-amount link and QR (the customer scans, enters the amount and pays). Query only: if the merchant does not have it enabled, it explains so and does not enable it. **Parameters** No parameters. **Returns** --- # Catalog > MCP tools to read your catalog of products and services. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/catalogo ## What it covers [#que-cubre] Read only: it lists the products or services you already created as payment links and returns the detail of one. ## Tools [#herramientas] ### List catalog of products or services [#tilopay-catalog-list-items] - `tilopay_catalog_list_items` - Access: Read only - API operation: `GET /api/v1/getLinkPaymentList/{api_key}/{limit}` — https://tilopay.com/en/developers/api/links-de-pago/payment-item-list Lists the products or services of the Tilopay catalog (title, price and short link). It can search by name or description (ignoring accents and case). The Tilopay listing may take a few minutes to reflect changes made in the dashboard; whatever is created from here appears immediately and the response of tilopay_catalog_create_item already includes the new item. Use refresh=true to force a fresh read. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | — | Maximum items (50 by default, 500 maximum) | | `search` | string | — | Texto a buscar en el nombre del producto | | `page` | integer | — | Página (por defecto 1) | | `refresh` | boolean | — | true para ignorar la memoria corta y leer de nuevo | | `include_qr` | boolean | — | Incluir el QR completo en base64 de cada ítem (por defecto false; el listado devuelve solo el enlace y has_qr) | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Catalog item detail [#tilopay-catalog-get-item] - `tilopay_catalog_get_item` - Access: Read only - API operation: `GET /api/v1/getLinkPaymentById/{link_payment_id}/{api_key}` — https://tilopay.com/en/developers/api/links-de-pago/payment-by-id Gets the detail of a product or service of the Tilopay catalog by its ID. The base64 QR only with include_qr=true. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `itemId` | string | yes | Catalog item ID | | `include_qr` | boolean | — | Incluir el QR en base64 (por defecto false; úsalo solo si el comercio pide el QR) | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Add a product or service to the catalog [#tilopay-catalog-create-item] - `tilopay_catalog_create_item` - Access: Write - API operation: `POST /api/v1/createLinkPayment` — https://tilopay.com/en/developers/api/links-de-pago/create-link-payment Creates a product or service in the Tilopay catalog with its price and returns the short payment link and the QR as an image. If the merchant sent a photo on WhatsApp in the last 10 minutes, it uses it as the product image. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Product or service name | | `amount` | number | yes | Price | | `currency` | string | — | Currency, e.g. CRC or USD. If omitted, the merchant's country currency is used. | | `description` | string | — | Product or service description | | `media_ref` | string | — | Referencia exacta de la imagen recibida por WhatsApp, si la conoce | | `use_last_image` | boolean | — | false para crear el producto sin foto aunque haya una imagen reciente | **Returns** `{ result, linkId, url }` result = the raw API response; linkId = id of the payment link representing the product; url = the product's charge link. ### Edit a catalog product [#tilopay-catalog-update-item] - `tilopay_catalog_update_item` - Access: Write - API operation: `POST /api/v1/createLinkPayment` — https://tilopay.com/en/developers/api/links-de-pago/create-link-payment Edits a product of the Tilopay catalog (name, price, currency, description or photo) without changing its payment link. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `itemId` | string | yes | ID del ítem del catálogo | | `name` | string | — | New name (or the same one) | | `amount` | number | — | New price (or the same one) | | `currency` | string | — | Currency; if omitted, the merchant's country currency | | `description` | string | — | New description (or the same one) | | `media_ref` | string | — | Referencia exacta de la imagen recibida por WhatsApp, si la conoce | | `use_last_image` | boolean | — | false para no cambiar la foto aunque haya una imagen reciente | **Returns** `{ result, linkId, url, replaced }` The API does not allow editing a link: the tool creates the new product and deletes the previous one. replaced = id of the product that was replaced; linkId and url = those of the new product. ### Delete a catalog product [#tilopay-catalog-delete-item] - `tilopay_catalog_delete_item` - Access: Sensitive - API operation: `POST /api/v1/deleteLinkPayment` — https://tilopay.com/en/developers/api/links-de-pago/delete-link-payment Deletes a product from the Tilopay catalog. The product's payment link stops working. Requires a confirmation code sent by email. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `itemId` | string | yes | ID del ítem del catálogo | | `confirmation_code` | string | — | Código de 6 dígitos enviado al correo del comercio | **Returns** `{ result }` result = API response deleting the payment link representing the product. ### Sync the catalog with social networks and search engines [#tilopay-catalog-feed] - `tilopay_catalog_feed` - Access: Write Delivers the synced catalog link and the steps to connect it in Facebook/Instagram, Google Shopping, TikTok or Pinterest (one channel at a time, the one the merchant asks for). With send_email=true it sends it to the merchant's email. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `platform` | string (facebook | google | tiktok | pinterest) | — | Canal pedido. Si el comercio no dijo cuál, NO llames: pregúntale primero. | | `send_email` | boolean | — | true si pidió que se lo mandes por correo | **Returns** --- # Contacts > MCP tools for your address book: view, save and delete contacts. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/contactos ## What it covers [#que-cubre] The address book lets the agent reuse phone and email when sending a payment link, without asking again. Deleting a contact is irreversible. ## Tools [#herramientas] ### Save contact [#tilopay-save-contact] - `tilopay_save_contact` - Access: Write Saves or updates a merchant contact (name, WhatsApp phone and email) to reuse it when sending links. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Contact name | | `phone` | string | — | WhatsApp phone, e.g. +50688887777 | | `email` | string | — | — | | `note` | string | — | — | **Returns** `{ contact }` The saved or updated contact. ### View contacts [#tilopay-list-contacts] - `tilopay_list_contacts` - Access: Read only Lists the merchant's saved contacts; it can filter by name. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `search` | string | — | Text to search in the name | **Returns** `{ contacts }` List of the merchant's saved contacts. ### Delete contact [#tilopay-delete-contact] - `tilopay_delete_contact` - Access: Sensitive Deletes a saved merchant contact by its name. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Contact name | **Returns** `{ removed }` removed is true. When no contact exists with that name, it returns an error. --- # Business profile and recipes > MCP tools to read and complete the business profile and to get automation recipes from it. - kind: mcp-tool - status: stable - access: write - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/perfil ## What it covers [#que-cubre] The agent keeps a short business profile: what the business does, what it sells, where it sells, what its charges look like and what it wants to improve. From that profile it builds the automation recipes it proposes to you, and it knows which question is still pending (the questionnaire is four questions at most). The profile is saved field by field: whatever is not sent is preserved. Recipes change with the modules the user has enabled: banking recipes only appear when the [banking API](/developers/agentes/mcp/api-bancario) is enabled. These tools do not call the Tilopay API: they work on the MCP server's own data. ## Tools [#herramientas] ### View business profile [#tilopay-get-business-profile] - `tilopay_get_business_profile` - Access: Read only Returns the business profile: the data the Tilopay account already has (name, line of business, location, phone, billing email, currencies, logo) and what is still missing to ask, with the next pending question. **Parameters** No parameters. **Returns** `{ profile, next_question, total_questions, is_complete }` profile = the saved business profile (or null if there is none); next_question = the next pending onboarding question; total_questions = how many questions the questionnaire has; is_complete = whether the profile is already complete. ### Save business profile [#tilopay-save-business-profile] - `tilopay_save_business_profile` - Access: Write Saves or completes the business profile with what the merchant answered or confirmed. It can save one field at a time; fields not sent are kept, and what is saved here is no longer overwritten by Tilopay data. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `business_type` | string | — | What the business does | | `main_products` | string | — | Main products or services | | `sales_channels` | string | — | Sales channels: in-store, online, social media, WhatsApp | | `ticket_pattern` | string | — | What the charges look like: amounts, frequency, currency | | `goals` | string | — | What the merchant wants to improve | | `notes` | string | — | Additional useful details | | `opening_hours` | string | — | Horario de atención | | `tone` | string | — | Tono de comunicación con los clientes | | `short_description` | string | — | Descripción corta del negocio | | `social_links` | string | — | Sitio web y redes sociales | | `refund_policy` | string | — | Política de devoluciones o reembolsos | | `business_name` | string | — | Corrección del nombre del negocio | | `industry` | string | — | Corrección del giro del negocio | | `phone` | string | — | Corrección del teléfono del negocio | | `address` | string | — | Corrección de la dirección | | `invoice_email` | string | — | Corrección del correo de facturación | **Returns** `{ profile, next_question, is_complete }` profile = the already updated profile (fields that are not sent are kept); next_question = the next pending question; is_complete = whether the profile became complete with this save. ### Recipes and automations [#tilopay-recommend-automations] - `tilopay_recommend_automations` - Access: Read only Returns suggested recipes and automations for the merchant based on their business profile and the active MCP modules. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | — | How many recipes to return (5 by default) | **Returns** `{ profile_complete, next_question, recipes }` recipes = suggested recipes and automations based on the business profile and active modules (includes banking ones only when the user has BaaS enabled); profile_complete and next_question indicate whether the profile still needs completing. --- # Micro-store and orders > MCP tools to build micro-stores with a cart from the catalog and manage the orders they generate. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/microtienda ## What it covers [#que-cubre] From the products in the [catalog](/developers/agentes/mcp/catalogo) the agent builds a **micro-store with a cart**: a public page with several products where the customer picks quantities and pays for everything at once. Each micro-store has its own link. The second half of the group is **order management**: it is set up once (store name, optional logo and the time of the daily reminder), and from then on you can see the summary, list orders by status, see the ones paid but not delivered and mark them as delivered. Order statuses are `pending` (awaiting payment), `paid` (paid, not delivered) and `delivered`. Deactivating a micro-store or turning off order management does not delete catalog products or orders already recorded. These tools do not call the Tilopay API: they work on the MCP server's own data, except the payment itself, which goes through each product's payment link. ## Tools [#herramientas] ### Create a micro-store with cart [#tilopay-store-create] - `tilopay_store_create` - Access: Write Creates a micro-store link to share with a customer: it shows the catalog products, the customer builds a cart and pays at the end from the same page with a link that includes the cart detail. By default it includes the whole catalog; it can be limited to certain product IDs. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `title` | string | — | Visible store title | | `item_ids` | array | — | Catalog product IDs to show; if omitted, the whole catalog is shown | **Returns** `{ slug, url, items }` slug = the micro-store identifier; url = public store link with cart; items = how many catalog products were included. ### List active micro-stores [#tilopay-store-list] - `tilopay_store_list` - Access: Read only Lists the merchant's active micro-stores with their sharing link. **Parameters** No parameters. **Returns** `{ stores[] }` stores = active micro-stores with slug, title, public link and the product ids each one includes. ### Disable a micro-store [#tilopay-store-delete] - `tilopay_store_delete` - Access: Sensitive Deactivates a micro-store by its slug: the link stops opening. Confirm with the merchant before using it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `slug` | string | yes | Store slug (the end of its link) | **Returns** `{ slug, active }` Disables the micro-store: the link stops showing products. active is set to false; the catalog products are not deleted. ### Connect a custom domain to the store [#tilopay-store-domain-connect] - `tilopay_store_domain_connect` - Access: Write Connects a merchant's own domain (e.g. tienda.minegocio.com) to their micro-store. It registers it immediately and returns the CNAME instruction. Use it only when the merchant gives the domain. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `hostname` | string | yes | Dominio, ej. tienda.minegocio.com | | `slug` | string | — | Slug de la tienda; si se omite, la más reciente | **Returns** ### Status of custom domains [#tilopay-store-domain-status] - `tilopay_store_domain_status` - Access: Read only Checks in Cloudflare the status of the custom domains of the merchant's stores. **Parameters** No parameters. **Returns** ### Remove a custom domain [#tilopay-store-domain-remove] - `tilopay_store_domain_remove` - Access: Sensitive Disconnects a custom domain from the store; the store stays at its tilopay.shop address. Confirm with the merchant before using it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `hostname` | string | yes | — | **Returns** ### Turn on or configure order management [#tilopay-orders-setup] - `tilopay_orders_setup` - Access: Write Turns on order tracking for the micro-store and saves the store name, the logo and the time of the daily reminder for paid, undelivered orders. It is also used to change any of that data later. The logo can be an https link to an image or the image the merchant just sent over WhatsApp (pass the reference shown in the message). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `store_name` | string | — | Visible store name | | `logo` | string | — | https link of the logo image, or the reference of the image sent over WhatsApp | | `reminder_time` | string | — | Local time of the daily reminder in HH:mm format (e.g. "18:00"); empty disables it | **Returns** `{ orders_enabled, store_name, has_logo, reminder_time }` Leaves order management on and returns the resulting configuration: the store name customers see, whether a logo is loaded and the time of the daily pending-orders reminder. ### View store and order settings [#tilopay-orders-settings] - `tilopay_orders_settings` - Access: Read only Shows whether order management is on, the store name, whether there is a logo and the time of the daily reminder. **Parameters** No parameters. **Returns** `{ orders_enabled, store_name, has_logo, reminder_time }` Current store and order configuration, without changing anything. ### Order summary [#tilopay-orders-status] - `tilopay_orders_status` - Access: Read only Summary of the micro-stores' orders: how many are pending payment, how many are paid and undelivered (with their amount) and how many were delivered today. **Parameters** No parameters. **Returns** `{ pending, paid, delivered, ... }` Order summary: how many are pending payment, paid and undelivered, and delivered, with the period's amounts. ### List orders [#tilopay-orders-list] - `tilopay_orders_list` - Access: Read only Lists the micro-stores' orders with their code, customer, total and detail. It can be filtered by status (pending = pending payment, paid = paid and undelivered, delivered = delivered) and by the last days. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `status` | string (pending | paid | delivered | all) | — | Order status; all by default | | `days` | integer | — | Only orders from the last N days | | `limit` | integer | — | — | **Returns** `{ orders[] }` orders = orders with code, customer, total, currency, status and date. It can be filtered by status (pending, paid, delivered, all) and by the last days. ### Paid, undelivered orders [#tilopay-orders-pending] - `tilopay_orders_pending` - Access: Read only Lists the orders already paid that have not been delivered yet. This is the tool used by the daily reminder. **Parameters** No parameters. **Returns** `{ pending, orders[] }` pending = how many orders are paid and undelivered; orders = those orders with code, customer, total and currency. ### Mark an order as delivered [#tilopay-order-mark-delivered] - `tilopay_order_mark_delivered` - Access: Write Marks one or several orders as delivered. It accepts the order code (e.g. "#A3F2", even several in the same message) or the customer name. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | — | Order code, e.g. "#A3F2"; several can be given | | `client` | string | — | Customer name of the order | **Returns** `{ delivered, missing }` delivered = how many orders were marked as delivered; missing = the codes that were not found. ### Turn off order management [#tilopay-orders-disable] - `tilopay_orders_disable` - Access: Sensitive Turns off order tracking and cancels the daily reminder. Already registered orders are kept. Ask for an explicit yes before using it. **Parameters** No parameters. **Returns** `{ orders_enabled: false }` Turns off order management. Already registered orders are kept. --- # Alerts and notifications > MCP tools to read and change your WhatsApp alerts: approved charges, declined charges, daily summary and orders. - kind: mcp-tool - status: stable - access: write - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/avisos ## What it covers [#que-cubre] The agent can notify you over WhatsApp when a charge comes in, when a charge is declined, when an order arrives, and send a daily summary at the chosen time. These two tools read and change those settings. They do not send messages on their own: they only turn alerts on and off and report the current settings. Fields that are not sent are preserved. ## Tools [#herramientas] ### View notification settings [#tilopay-get-notification-settings] - `tilopay_get_notification_settings` - Access: Read only Shows how the merchant's notifications are configured: whether payments received are notified over WhatsApp and/or email, which email, from which amount and whether there is a quiet-hours window. **Parameters** No parameters. **Returns** `{ settings }` settings = the merchant's notification configuration: which WhatsApp notifications are on (approved charges, failed charges, daily summary, orders) and what time the daily summary is sent. ### Configure the merchant's notifications [#tilopay-set-notification-settings] - `tilopay_set_notification_settings` - Access: Write Changes the merchant's notification settings: turn on or off the payment-received notification over WhatsApp and email, the destination email, the minimum amount to notify and the quiet-hours window (in the merchant's local time). Only send the fields the user asked to change. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `payment_whatsapp` | boolean | — | Notify over WhatsApp when a payment is received | | `payment_email` | boolean | — | Notify over email when a payment is received | | `notify_email` | string | — | Email that receives the notifications (if omitted, the account's) | | `min_amount` | number | — | Minimum amount to notify (0 = notify for any amount) | | `quiet_from` | integer | null | — | Local time when the quiet window starts (null to remove it) | | `quiet_to` | integer | null | — | Local time when the quiet window ends (null to remove it) | **Returns** `{ settings }` settings = the already updated notification configuration. Fields that are not sent are kept. --- # Payment receipts > MCP tools to list receipts of approved charges and resend a customer the receipt for their payment. - kind: mcp-tool - status: stable - access: write - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/comprobantes ## What it covers [#que-cubre] Every approved charge that goes through the agent produces a receipt with its own code, a page to view it and an image to share. With these two tools you list the receipts for a period and resends one to the customer over WhatsApp. The receipt is an informational document about the charge. It is not an electronic invoice. ## Tools [#herramientas] ### List payment receipts [#tilopay-list-receipts] - `tilopay_list_receipts` - Access: Read only Lists the merchant's latest payment receipts (amount, customer, order and code). Each one includes a text ready to forward to the customer. It does not charge anything. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | — | How many receipts to list (5 by default) | **Returns** `{ receipts[] }` receipts = payment receipts with code, amount, currency, customer, order number, payment date, receipt page link and image link. ### Resend a payment's receipt to the customer [#tilopay-send-receipt] - `tilopay_send_receipt` - Access: Write Sends the customer the receipt of a charge, by WhatsApp (by default: a one-tap wa.me link) or by email with the merchant's branding (channel=email). It searches by code, order number or customer name; without data it uses the most recent receipt. If the user says 'the last charge' or 'the last receipt', call it directly without code or order_number (do not query transactions first). Use send_now only if the user asks for it to be sent directly by WhatsApp. Never make up the customer's email or phone: if they are missing, ask for them. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | — | Receipt code (8 characters) | | `order_number` | string | — | Número de orden del cobro | | `client_name` | string | — | Nombre del cliente del cobro, para buscar el comprobante | | `channel` | string (whatsapp | email) | — | whatsapp (por defecto) o email | | `email` | string | — | Correo del cliente, si se envía por correo | | `contact_name` | string | — | Name of the saved contact or of the customer | | `phone` | string | — | WhatsApp phone, e.g. +50688887777 | | `message` | string | — | Your own text to go along with the receipt | | `save_contact` | boolean | — | Save the contact in the address book | | `send_now` | boolean | — | true sends the receipt directly to the customer; by default the wa.me link is delivered | **Returns** `{ sent | code, page_url, image_url, whatsapp_url }` Resends the receipt to the customer over WhatsApp: it returns the receipt code, its page link, the image link and, when not sent directly, the wa.me link ready to open. --- # Reminders > MCP tools to create, list and cancel WhatsApp reminders, with or without an agent action when they fire. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/recordatorios ## What it covers [#que-cubre] A reminder is a scheduled WhatsApp message: one-off, daily, weekly or monthly. It can be plain text or carry an **action** the agent runs when it fires, written in natural language ("give me today's sales summary", "list the pending payment links"), and the result is attached to the message. Rules worth knowing: - Times are your local time, based on the country of their WhatsApp number. - There is a maximum of 20 active reminders. - Reminders about payment links or charges **always** go to your WhatsApp, never to the customer. - To send to another number, save it first as a [contact](/developers/agentes/mcp/contactos). ## Tools [#herramientas] ### Create reminder [#tilopay-create-reminder] - `tilopay_create_reminder` - Access: Write Creates a WhatsApp reminder, one-time or recurring (daily, weekly or monthly). It can be text only or run an agent action when sent (with `action`): sales summary, the day's transactions, pending links, etc. It does NOT create banking reminders (balance, movements, statements, transfers): the merchant configures those in the calendar of the Banca Cliente portal. If the reminder is to send a payment link or charge, it ALWAYS goes to the WhatsApp of the merchant who asked for it, never to the customer. In other cases it is sent to the merchant's WhatsApp or to an already saved contact (contact_name); if the user gives a new phone, it must first be saved with tilopay_save_contact. Maximum 20 active reminders. The message goes out with a fixed prefix that identifies the merchant. Times in the merchant's local time (according to the country of their WhatsApp phone). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `message` | string | yes | Reminder text (max. 500 characters, no HTML) | | `action` | string | — | Instruction the agent will run when sending it, in natural language, e.g. 'give me today's sales summary' or 'list pending payment links'. The result is attached to the message. Omit it for a plain-text reminder. | | `schedule` | string (once | daily | weekly | monthly) | yes | Frequency: once = one time only | | `when` | string | — | Only for once: local date and time 'YYYY-MM-DD HH:mm' | | `time_of_day` | string | — | For recurring ones: local time 'HH:mm' | | `weekday` | integer | — | For weekly: 0 = Sunday ... 6 = Saturday | | `day_of_month` | integer | — | For monthly: day of the month (1-28) | | `contact_name` | string | — | Name of an already saved contact (mcp_contact) who should receive it. If omitted, it reaches the merchant's WhatsApp. New phone numbers are not accepted: save them first with tilopay_save_contact. | **Returns** `{ id, message, action, phone, contact_name, schedule, when, next_run_local, is_active }` The created reminder: when it is sent in the merchant's local time, to which number, and what agent action runs when it is sent. Maximum 20 active reminders; charge-link ones always reach the merchant's WhatsApp, never the customer's. ### View reminders [#tilopay-list-reminders] - `tilopay_list_reminders` - Access: Read only Lists the merchant's active reminders with their next send date. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `include_inactive` | boolean | — | true to include those already sent or cancelled | **Returns** `{ reminders[] }` reminders = the merchant's reminders with their message, frequency, next run in local time and whether they are active. ### Cancel reminder [#tilopay-cancel-reminder] - `tilopay_cancel_reminder` - Access: Sensitive Cancels a reminder by its ID or by searching part of its text. It does not delete the history, it just stops being sent. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | — | Reminder ID | | `search` | string | — | Part of the reminder's text | **Returns** `{ cancelled }` cancelled = the reminder that was deactivated. --- # Recurring billing > MCP tools to read recurring plans, subscribers and their payments, and to pause, reactivate or delete a subscriber. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/recurrentes ## What it covers [#que-cubre] Four read-only tools over recurring billing plans and their subscribers, plus one sensitive tool that pauses, reactivates or deletes a subscriber. That last one affects future charges: ask for human confirmation before running it. ## Tools [#herramientas] ### List recurring plans [#tilopay-recurring-list-plans] - `tilopay_recurring_list_plans` - Access: Read only - API operation: `POST /api/v1/getPlansRepeat` Lists the recurring billing plans configured in Tilopay. **Parameters** No parameters. **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Recurring plan detail [#tilopay-recurring-get-plan] - `tilopay_recurring_get_plan` - Access: Read only - API operation: `POST /api/v1/getPlanRepeat` Returns the detail of a recurring billing plan by its ID. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Plan ID | | `include_qr` | boolean | — | Incluir el QR en base64 del plan (por defecto false; úsalo solo si el comercio pide el QR) | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Subscriber detail [#tilopay-recurring-get-subscriber] - `tilopay_recurring_get_subscriber` - Access: Read only - API operation: `POST /api/v1/getSuscriptorRepeat` Returns the detail of a subscriber of a recurring plan by its ID. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Subscriber ID | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Find a subscriber by name or email [#tilopay-recurring-find-subscriber] - `tilopay_recurring_find_subscriber` - Access: Read only Searches for a subscriber by name or email across all recurring plans at once. Use it before pausing, reactivating or editing someone when you do not have their ID. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Nombre o correo del suscriptor | **Returns** ### Subscriber payments [#tilopay-recurring-subscriber-payments] - `tilopay_recurring_subscriber_payments` - Access: Read only - API operation: `POST /api/v1/getSuscriptorPayments` Lists the payments made by a recurring subscriber. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Subscriber ID | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Failed recurring charges [#tilopay-recurring-failed-payments] - `tilopay_recurring_failed_payments` - Access: Read only - API operation: `POST /api/v1/getSuscriptorPayments` Lists the latest recurring charges that failed (plan, subscriber, amount, date and reason in plain language). A single call is enough. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | — | Maximum charges (10 by default) | | `startDate` | string | — | From, "YYYY-MM-DD HH:mm:ss" | | `endDate` | string | — | To, "YYYY-MM-DD HH:mm:ss" | **Returns** `{ total, source, failures[] }` failures = declined recurring charges, with plan, subscriber, amount, currency and decline reason; total = how many were found; source = the API route they were obtained from. It combines the query of plans, subscribers and their payments. ### Pause, reactivate or delete a subscriber [#tilopay-recurring-manage-subscriber] - `tilopay_recurring_manage_subscriber` - Access: Sensitive - API operation: `POST /api/v1/pauseSuscriptorRepeat, /reactiveSuscriptorRepeat o /deleteSuscriptorRepeat` Pauses, reactivates or deletes a subscriber of a recurring plan in Tilopay. Sensitive operation: it affects future charges. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `idSubscriber` | string | yes | Subscriber ID | | `action` | string (pause | reactivate | delete) | yes | Action to perform | | `confirmation_code` | string | — | 6-digit code received by email to authorize the operation | **Returns** `{ result }` Raw response of the endpoint matching the action: pause, reactivate or delete. ### Create subscription plan [#tilopay-recurring-create-plan] - `tilopay_recurring_create_plan` - Access: Write - API operation: `POST /api/v1/createPlanRepeat` Creates a recurring billing plan (subscription) in Tilopay with its title, currency, frequency and modalities (name and amount). Returns the ID of the created plan; then use tilopay_recurring_subscription_url to get the link customers use to subscribe. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `title` | string | yes | Plan title | | `modality` | array | yes | Plan modalities (at least one): name and amount per cycle | | `frequency` | string (diario | semanal | mensual | anual | quincenal | bimestral | trimestral | cuatrimestral | semestral) | yes | Charge frequency | | `description` | string | — | Plan description | | `currency` | string | — | ISO 4217 currency, e.g. USD or CRC. If omitted, the merchant's country currency is used. | | `first_amount` | number | — | Amount of the initial payment (0 if there is none) | | `trial_days` | integer | — | Free trial days (0 if there is no trial) | | `attempts` | integer | — | Retries on a failed charge (1 by default) | | `end_at` | string | — | Plan end date in DD-MM-YYYY format (optional) | | `thanks_url` | string | — | Thank-you URL after subscribing (optional) | | `webhook_subscribe` | string | — | Webhook when a customer subscribes (optional) | | `webhook_payment` | string | — | Webhook when a payment is charged (optional) | | `webhook_rejected` | string | — | Webhook when a charge is declined (optional) | | `webhook_unsubscribe` | string | — | Webhook when a subscription is cancelled (optional) | | `webhook_reactive` | string | — | Webhook when a subscription is reactivated (optional) | **Returns** `{ result, currency, frequency }` result = API response with the created plan; currency and frequency = the currency and frequency the plan ended up with. ### Edit subscription plan [#tilopay-recurring-edit-plan] - `tilopay_recurring_edit_plan` - Access: Write - API operation: `POST /api/v1/editPlanRepeat` Modifies an existing recurring billing plan: title, description, frequency, currency, amount per cycle (modality), initial payment, free trial, retries, status or end date. First check tilopay_recurring_get_plan to know the current values. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Plan ID | | `title` | string | yes | Plan title | | `frequency` | string (diario | semanal | mensual | anual | quincenal | bimestral | trimestral | cuatrimestral | semestral) | yes | Charge frequency | | `modality` | array | — | Modalities with their amount per cycle; send them when the plan price changes | | `currency` | string | — | ISO 4217 currency | | `description` | string | — | Plan description | | `first_amount` | number | — | Amount of the initial payment | | `trial_days` | integer | — | Free trial days | | `attempts` | integer | — | Retries on a failed charge | | `status` | string (inactive | active | closed_to_new) | — | Status: inactive, active or closed_to_new (active with no new sign-ups) | | `end_at` | string | — | Plan end date in DD-MM-YYYY format (optional) | | `thanks_url` | string | — | Thank-you URL | | `webhook_subscribe` | string | — | Webhook on subscribe | | `webhook_payment` | string | — | Webhook when a payment is charged | | `webhook_rejected` | string | — | Webhook when a charge is declined | | `webhook_unsubscribe` | string | — | Webhook on cancellation | | `webhook_reactive` | string | — | Webhook on reactivation | **Returns** `{ result }` result = API response editing the plan. Fields that are not sent are kept. ### Delete subscription plan [#tilopay-recurring-delete-plan] - `tilopay_recurring_delete_plan` - Access: Sensitive - API operation: `POST /api/v1/deletePlanRepeat` Deletes a recurring billing plan in Tilopay. It affects the plan's future charges: confirm with the merchant before running it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Plan ID | **Returns** `{ result }` result = API response deleting the plan. The plan's subscribers stop being charged. ### Subscription link for a plan [#tilopay-recurring-subscription-url] - `tilopay_recurring_subscription_url` - Access: Read only - API operation: `POST /api/v1/recurrentUrl` Returns the link a customer uses to sign up for a recurring plan (or renew it if the email is already subscribed). Deliver the full URL, without shortening it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Plan ID | | `email` | string | — | Customer email; if already subscribed it returns the renewal link | **Returns** `{ url, result }` url = subscription link for the plan, to share with the customer; result = the raw API response. ### Edit a plan's subscriber [#tilopay-recurring-edit-subscriber] - `tilopay_recurring_edit_subscriber` - Access: Sensitive - API operation: `POST /api/v1/editSuscriptorRepeat` Changes a subscriber's status (active, paused or deleted) and/or their expiration date. It affects future charges: confirm with the merchant before running it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Subscriber ID | | `status` | string (active | paused | deleted) | — | New subscriber status | | `expire` | string | — | Plan expiration date for that subscriber in YYYY-MM-DD format | **Returns** `{ result }` result = API response editing the subscriber's data (email, phone, amount or expiration date, depending on the plan). ### Create discount coupon [#tilopay-recurring-create-coupon] - `tilopay_recurring_create_coupon` - Access: Write - API operation: `POST /api/v1/createCoupon` Creates a discount coupon for a subscription plan: percentage or fixed amount, expiration date, allowed emails and usage limits. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `planId` | string | yes | Recurring plan ID | | `discount_type` | string (percentage | fixed) | yes | Discount type: percentage or fixed | | `discount` | number | yes | Discount value | | `expire` | string | yes | Coupon expiration date in YYYY-MM-DD format | | `allow_existing_users` | boolean | — | true if already registered customers can also use it (false = new customers only) | | `emails` | array | — | Authorized emails; if omitted, any email can use it | | `usage` | integer | — | Total number of uses | | `renews` | integer | — | Renewals valid with the coupon | | `renews_by_user` | integer | — | Uses per single customer | **Returns** `{ result }` result = API response with the discount coupon created for the plan. ### A plan's coupons [#tilopay-recurring-list-coupons] - `tilopay_recurring_list_coupons` - Access: Read only - API operation: `POST /api/v1/getRepeatCoupons` Lists the discount coupons associated with a subscription plan. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `planId` | string | yes | Recurring plan ID | **Returns** `{ result }` result = the plan's coupons as returned by the API. ### Coupon detail [#tilopay-recurring-get-coupon] - `tilopay_recurring_get_coupon` - Access: Read only - API operation: `POST /api/v1/getCoupon` Returns the detail of a discount coupon by its ID. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Coupon ID | **Returns** `{ result }` result = coupon detail as returned by the API. ### Delete a coupon [#tilopay-recurring-delete-coupon] - `tilopay_recurring_delete_coupon` - Access: Sensitive - API operation: `POST /api/v1/deleteCoupon` Deletes a discount coupon of a subscription plan. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Coupon ID | **Returns** `{ result }` result = API response deleting the coupon. ### Start step by step for a subscription plan [#tilopay-recurring-plan-wizard-start] - `tilopay_recurring_plan_wizard_start` - Access: Write Starts the guided step-by-step to create (mode=create) or edit (mode=edit with plan_id) a subscription plan. Returns the first pending question: deliver it to the merchant as is and pass their answer to tilopay_recurring_plan_wizard_answer. Use it instead of tilopay_recurring_create_plan when some data is missing. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `mode` | string (create | edit) | yes | create for a new plan, edit to modify an existing one | | `plan_id` | string | — | Plan ID (required in mode=edit) | | `title` | string | — | Title, if the merchant already gave it | | `frequency` | string (diario | semanal | quincenal | mensual | bimestral | trimestral | cuatrimestral | semestral | anual) | — | Frequency, if the merchant already gave it | | `amount` | number | — | Amount per cycle, if the merchant already gave it | | `currency` | string | — | ISO currency if the merchant indicated it with a symbol or name ($ or dollars = USD, ₡ or colones = CRC) | | `description` | string | — | Description, if already given | **Returns** `{ draft }` draft = the plan draft in progress, with what has already been answered and the next pending step. It creates nothing in Tilopay until confirmed. ### Answer a step of the subscription plan [#tilopay-recurring-plan-wizard-answer] - `tilopay_recurring_plan_wizard_answer` - Access: Write Saves the merchant's answer to the current step of the subscription plan and returns the next question or the summary to confirm. Deliver the returned text as is, without adding questions of your own. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `answer` | string | yes | What the merchant answered, as is | **Returns** `{ draft }` draft = the updated draft with the step's answer and the next pending step. ### Confirm the subscription plan [#tilopay-recurring-plan-wizard-confirm] - `tilopay_recurring_plan_wizard_confirm` - Access: Write - API operation: `POST /api/v1/createPlanRepeat` Creates or edits the subscription plan with the step-by-step data, after the merchant said yes to the summary. When creating, it also returns the subscription link: deliver it in full, without shortening it. **Parameters** No parameters. **Returns** `{ plan_id, subscription_url }` Creates the plan with what was built step by step: plan_id = id of the created plan; subscription_url = subscription link to share. ### Cancel the plan step by step [#tilopay-recurring-plan-wizard-cancel] - `tilopay_recurring_plan_wizard_cancel` - Access: Write Discards the subscription plan that was in progress without creating or changing anything. **Parameters** No parameters. **Returns** Discards the plan draft in progress. It creates or changes nothing in Tilopay; it only returns the confirmation in text. ### Payment link to settle a failed recurring charge [#tilopay-recurring-settle-link] - `tilopay_recurring_settle_link` - Access: Write - API operation: `POST /api/v1/createLinkPayment` — https://tilopay.com/en/developers/api/links-de-pago/create-link-payment Creates a payment link for the amount of a recurring charge that failed and associates it with the plan's subscriber. When the customer pays that link, the subscription is settled automatically: the subscriber is reactivated and its expiration date moves to the next cycle. Use it when the merchant agrees to send the link to the customer of a failed charge (the data comes from tilopay_recurring_failed_payments). Returns the link URL and, if there is a phone number, a WhatsApp link ready to send to the customer. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `subscriber_id` | string | yes | ID of the subscriber with the failed charge | | `amount` | number | yes | Outstanding amount of the failed charge | | `currency` | string | — | Currency of the charge. If omitted, the merchant's country currency is used. | | `plan_id` | string | — | Subscription plan ID | | `plan_name` | string | — | Plan name, for the charge's concept | | `frequency` | string (diario | semanal | quincenal | mensual | bimestral | trimestral | cuatrimestral | semestral | anual) | — | Plan frequency; if omitted it is queried from the plan or assumed monthly | | `client_name` | string | — | Customer name | | `client_email` | string | — | Customer email | | `client_phone` | string | — | Customer phone in international format, for the WhatsApp link | | `message` | string | — | Your own message for the customer; if omitted one is drafted | **Returns** `{ url, linkId, whatsapp_url, currency, amount, subscriber_id, frequency }` Creates a one-time payment link for the subscriber to settle a failed recurring charge: url = payment link; whatsapp_url = WhatsApp link ready to send it; linkId = id of the created link. ### Links to bring several failed charges up to date [#tilopay-recurring-settle-batch] - `tilopay_recurring_settle_batch` - Access: Write Batch sending (maximum 20) of links to bring failed recurring charges up to date. First call it WITHOUT confirming: it returns the summary (count, total, who has no phone) and a fingerprint. Show that summary AS IS and ask for an explicit yes. Only if the merchant answers yes, call it again with confirm=true and the SAME fingerprint. Use it when they ask to 'send it to everyone'; for a single customer use tilopay_recurring_settle_link. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `confirm` | boolean | — | true solo tras el sí explícito al resumen | | `fingerprint` | string | — | Huella devuelta por el resumen | | `subscriber_ids` | array | — | Limitar a estos suscriptores; si se omite, todos los pendientes (máx. 20) | **Returns** ### Settle a subscriber [#tilopay-recurring-mark-settled] - `tilopay_recurring_mark_settled` - Access: Write - API operation: `POST /api/v1/editSuscriptorRepeat` Marks the subscriber as up to date on their plan: it leaves them active and moves their expiration date to the next cycle. Use it when the pending charge was already paid by other means. If the payment was made with the tilopay_recurring_settle_link link, it is not needed: it updates by itself. Confirm with the merchant before running it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `subscriber_id` | string | yes | Subscriber ID | | `plan_id` | string | — | Subscription plan ID | | `frequency` | string (diario | semanal | quincenal | mensual | bimestral | trimestral | cuatrimestral | semestral | anual) | — | Plan frequency; if omitted it is queried from the plan or assumed monthly | | `expire` | string | — | New expiration date YYYY-MM-DD; if omitted the next cycle is calculated | **Returns** `{ ok, expire, frequency }` Moves the subscriber's due date forward according to the plan's frequency, to settle them after recovering a failed charge. expire = the new expiration date. --- # Stored cards and bulk collections > MCP tools to store a customer card, charge it, remove it and work with bulk collection groups. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/tarjetas-guardadas ## What it covers [#que-cubre] This group works with the cards your customers already authorised to store (tokenization) and with bulk collections against those cards. How storing works: the agent generates a **secure link** and shares it with the customer; the customer types their card there. Neither the agent, nor the model, nor you see the full number: the read tools return the brand and the last digits. The sensitive operations in this group — charging a stored card, removing a card and creating bulk collections — require a **confirmation code** sent to your email. The API routes used by bulk collections live outside the portal reference, so these pages do not publish them. What is documented is what each tool does, what it takes and what it returns. ## Tools [#herramientas] ### List collection groups [#tilopay-saved-cards-list-groups] - `tilopay_saved_cards_list_groups` - Access: Read only Lists the collection groups with stored cards (affiliates) in Tilopay. **Parameters** No parameters. **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### List affiliates with a stored card [#tilopay-saved-cards-list-affiliates] - `tilopay_saved_cards_list_affiliates` - Access: Read only Lists the affiliates (customers with a stored card) of a collection group. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `group` | integer | — | Group ID (0 for all) | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### List bulk collections [#tilopay-saved-cards-list-collections] - `tilopay_saved_cards_list_collections` - Access: Read only Lists the bulk collections made with stored cards in Tilopay. **Parameters** No parameters. **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Bulk collection detail [#tilopay-saved-cards-collection-detail] - `tilopay_saved_cards_collection_detail` - Access: Read only Returns the detail of a bulk collection. It requires the collection `code` (the `code` field returned by tilopay_saved_cards_list_collections). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | Bulk collection code (the `code` field of the collections list) | **Returns** `{ result }` Raw Tilopay API response under the `result` key. ### Save a customer's card (tokenize) [#tilopay-tokenize-card] - `tilopay_tokenize_card` - Access: Write - API operation: `POST /api/v1/processTokenize` Generates the secure Tilopay link where the customer enters their card to have it saved (tokenized). The merchant never sees or receives the card data. Once saved, it can be charged with tilopay_saved_cards_create_payments (that one does ask for a confirmation code by email). If a contact or phone is given, it also returns a WhatsApp link with the message ready to send to the customer. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `email` | string | yes | Cardholder's email | | `first_name` | string | yes | Cardholder's first name | | `last_name` | string | yes | Cardholder's last name | | `language` | string (es | en) | — | Form language (es by default) | | `redirect_url` | string | — | URL the customer returns to when finished (optional) | | `contact_name` | string | — | Name of the saved contact or of the customer, to draft the WhatsApp message | | `phone` | string | — | Customer's WhatsApp phone, e.g. +50688887777 | | `message` | string | — | Your own text for the message to the customer | | `save_contact` | boolean | — | Save the contact in the address book | **Returns** `{ tokenize_url, whatsapp_url, email, contact }` tokenize_url = secure link where the customer enters their card to save it; whatsapp_url = WhatsApp link ready to send it. The card is typed by the customer: the agent never receives the number. ### List a customer's saved cards [#tilopay-list-customer-cards] - `tilopay_list_customer_cards` - Access: Read only Lists the cards a customer already has stored (tokenized) in Tilopay, identified by your email. Returns the token of each card, which is what tilopay_charge_saved_card needs to charge it. Only cards stored with the link of this assistant or of the MCP can be charged. It does not charge anything. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `email` | string | yes | Email of the customer who owns the cards | | `currency` | string | — | Reference currency, e.g. CRC or USD. If omitted, the merchant's country currency is used. | **Returns** `{ cards, count }` cards = that customer's already saved cards, with brand, last digits and their identifier to charge; count = how many there are. It never returns the full card number. ### Available payment methods and installments [#tilopay-payment-methods] - `tilopay_payment_methods` - Access: Read only Lists the payment methods enabled for the merchant in the Tilopay checkout (cards, SINPE Móvil, Yappy, installments or zero rate when active) and whether the account responds in production or test. Use it when the merchant asks what they can offer their customer or whether they have installments / zero rate. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `amount` | number | — | Reference amount; some methods and installments depend on the amount | | `currency` | string | — | Reference currency. If omitted, the merchant's country currency is used. | | `email` | string | — | Customer email (optional; only to also see their saved cards) | **Returns** `{ methods, installments, cards, environment, currency }` methods = payment methods enabled for the merchant; installments = installments available when the method allows them; cards = customer's saved cards if their email was sent; environment = production or test. ### Charge a customer's saved card [#tilopay-charge-saved-card] - `tilopay_charge_saved_card` - Access: Sensitive - API operation: `POST /api/v1/processRecurrentPayment` Charges an amount to a customer's already saved (tokenized) card. Requires the customer's email and the card token (obtained with tilopay_list_customer_cards). Sensitive operation: it moves real money and requires a second factor. Call it first without `confirmation_code`: a 6-digit code is sent to the merchant's email and the response carries `requires_confirmation`. Repeat the same call, with the same parameters, adding `confirmation_code`. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `email` | string | yes | Cardholder's email | | `card` | string | yes | Token of the saved card (v2 version) | | `amount` | number | yes | Amount to charge | | `currency` | string | — | Currency of the charge. If omitted, the merchant's country currency is used. | | `order_number` | string | — | Order number; if omitted a unique one is generated | | `capture` | boolean | — | Capture immediately (true by default) | | `confirmation_code` | string | — | 6-digit code received by email to authorize the operation | **Returns** `{ result, approved, orderNumber, currency, receipt_code, receipt_url, receipt_image_url }` Charges an already saved card of the customer: approved = whether the charge was approved; orderNumber = generated order number; receipt_* = the charge's receipt when generated. Requires a second factor by email. ### Delete a customer's saved card [#tilopay-remove-saved-card] - `tilopay_remove_saved_card` - Access: Sensitive - API operation: `POST /api/v1/user/card-remove` Deletes a customer's saved card (token) in Tilopay. Sensitive operation: requires a second factor. Call it first without `confirmation_code` and repeat it later with the 6-digit code sent to the merchant's email. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `email` | string | yes | Email of the customer who owns the card | | `token` | string | yes | Token of the card to delete | | `confirmation_code` | string | — | 6-digit code received by email to authorize the operation | **Returns** `{ result }` result = API response deleting the customer's saved card. Requires a second factor by email. ### Charge stored cards [#tilopay-saved-cards-create-payments] - `tilopay_saved_cards_create_payments` - Access: Sensitive Creates charges to affiliates and/or groups with stored cards in Tilopay. Sensitive operation: it moves real money. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `reason` | string | yes | Reason for the charge, e.g. "Monthly fee" | | `capture` | boolean | — | Capture immediately (true by default) | | `users` | array | — | Individual affiliates to charge. Each item: id (string, required), amount (number, required, greater than zero), currency (string, required), date (string "YYYY-MM-DD", empty = immediate) | | `groups` | array | — | Groups to charge. Each item: id (string, required), amount per affiliate (number, required, greater than zero), currency (string, required), date (string "YYYY-MM-DD", empty = immediate) | | `confirmation_code` | string | — | 6-digit code received by email to authorize the operation | **Returns** `{ result }` Raw Tilopay API response under the `result` key. --- # Settlements, deposits and billing > MCP tools to query the next settlement, the settlement history, security deposits and the invoices Tilopay issues to you. - kind: mcp-tool - status: stable - access: write - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/liquidaciones ## What it covers [#que-cubre] This group covers the money Tilopay settles to you: the pending balance and the date of the next deposit, already processed settlements with their transactions, security deposits (pre-authorizations held on customers) and the invoices Tilopay issues for using the platform. Everything is read-only except extending a security deposit, which requires a six-digit code sent by email. ## Tools [#herramientas] ### Next settlement and balance to settle [#tilopay-settlement-balance] - `tilopay_settlement_balance` - Access: Read only OFFICIAL Tilopay data: date of the next deposit (next_settlement), settlement frequency, destination IBAN account and, per currency, the accumulated pending balance with its breakdown (subtotal, commission, iva_commission, retention, retention_iva, cost, iva_tilopay, transfer, transfer_iva, net total). **Parameters** No parameters. **Returns** ### Settlement history [#tilopay-settlements-list] - `tilopay_settlements_list` - Access: Read only Settlements already processed by Tilopay: deposit date (date_to_report), period, gross amount, deductions and total transferred. Use it for 'did they deposit Friday's money yet?' or 'how much did they deposit last month?'. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `page` | integer | — | Página (por defecto 1) | | `limit` | integer | — | Registros por página (por defecto 20) | | `search` | string | — | Texto a buscar | **Returns** ### Transactions of a settlement [#tilopay-settlement-transactions] - `tilopay_settlement_transactions` - Access: Read only Transactions included in an already processed settlement (id obtained from tilopay_settlements_list). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `settlement_id` | string | yes | Id de la liquidación | **Returns** ### Security deposits (pre-authorizations) [#tilopay-deposits-list] - `tilopay_deposits_list` - Access: Read only Security deposits or pre-authorizations held on customers (car rental, hospitality, security deposits): amount, status and expiration. Not to be confused with settlements. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `page` | integer | — | Página (por defecto 1) | | `limit` | integer | — | Registros por página (por defecto 20) | | `search` | string | — | Texto a buscar | **Returns** ### Detail of a security deposit [#tilopay-deposit-detail] - `tilopay_deposit_detail` - Access: Read only Detail of a security deposit (id from tilopay_deposits_list). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `deposit_id` | string | yes | Id del depósito | **Returns** ### Extend a security deposit [#tilopay-deposit-extend] - `tilopay_deposit_extend` - Access: Write Extends the validity of the authorization of a security deposit. Requires the six-digit code sent by email. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `deposit_id` | string | yes | Id del depósito | **Returns** ### Tilopay service invoices [#tilopay-billing-list] - `tilopay_billing_list` - Access: Read only Invoices Tilopay issues to the merchant for using the platform (they are not the merchant's sales). Use it for 'how much did Tilopay charge me this month?' or 'where is my Tilopay invoice?'. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `page` | integer | — | Página (por defecto 1) | | `limit` | integer | — | Registros por página (por defecto 20) | | `search` | string | — | Texto a buscar | **Returns** --- # Account, merchant and team > MCP tools to link or unlink the account, choose the active merchant, manage the assistant's employees and query dashboard users and enabled payment methods. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/cuenta ## What it covers [#que-cubre] This group covers the Tilopay account linked to the assistant: linking or unlinking it, choosing the active merchant when there are several, giving or removing employee access, and querying the web dashboard users and the enabled payment methods and currencies. Unlinking the account and removing an employee's access are marked as sensitive and ask for explicit confirmation before running. ## Tools [#herramientas] ### Selected merchant [#tilopay-switch-commerce] - `tilopay_switch_commerce` - Access: Write Shows the active merchant of the linked Tilopay account and lets you switch it when the account has several. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `commerce_id` | string | — | Comercio que se quiere activar. Si se omite, solo se muestra el actual. | **Returns** ### Unlink my account [#tilopay-unlink-account] - `tilopay_unlink_account` - Access: Sensitive Unlinks the Tilopay account from the assistant: revokes ALL access (WhatsApp, Claude and other applications), deletes the stored keys (including the bank ones) and closes the banking sessions. Before calling it with confirm=true, repeat that scope to the merchant and ask for confirmation. Use it when the merchant asks to unlink, disconnect or remove their account from the assistant. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `confirm` | boolean | yes | Debe ser true: el comercio confirmó que quiere desvincular tu cuenta. | | `all_channels` | boolean | — | true solo si el comercio pidió expresamente desvincular también el otro canal (WhatsApp y clientes MCP). | **Returns** ### Link my account [#tilopay-send-link-flow] - `tilopay_send_link_flow` - Access: Write Sends the merchant, on WhatsApp, the button to link (or relink) their Tilopay account with the assistant. Use it for any request to link, connect or activate the assistant with the Tilopay account. **Parameters** No parameters. **Returns** ### Give an employee access [#tilopay-invite-employee] - `tilopay_invite_employee` - Access: Write Invites an employee to use the assistant with their own Tilopay user (owner only). Asks for the employee's name and WhatsApp phone. Returns a wa.me link for the owner to send the invitation. In your answer, first repeat the `prerequisito` line verbatim and then deliver the link. The employee can query sales, create charges and send receipts; they cannot refund, charge stored cards, create or delete products, change prices or change the configuration. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | Nombre del empleado | | `phone` | string | yes | WhatsApp del empleado, ej. 8888-8888 | **Returns** ### View my employees [#tilopay-list-employees] - `tilopay_list_employees` - Access: Read only Lists the employees with access to the assistant and the pending invitations (owner only). **Parameters** No parameters. **Returns** ### Remove an employee's access [#tilopay-remove-employee] - `tilopay_remove_employee` - Access: Sensitive Removes an employee's access to the assistant (owner only): revokes their whole session, or cancels the invitation if they had not linked yet. Before calling it with confirm=true, confirm with the owner who is being removed. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | — | Nombre del empleado | | `phone` | string | — | Teléfono del empleado | | `confirm` | boolean | yes | true: el dueño confirmó quitarle el acceso | **Returns** ### Enabled payment methods and currencies [#tilopay-accepted-payment-methods] - `tilopay_accepted_payment_methods` - Access: Read only Payment methods the merchant has enabled in Tilopay (card brands, SINPE, etc.), configured currencies and PayFac configuration. Use it for 'which cards do I accept?', 'is SINPE active?', 'can I charge in dollars?'. **Parameters** No parameters. **Returns** ### Tilopay dashboard users [#tilopay-panel-users] - `tilopay_panel_users` - Access: Read only Users with access to the merchant's Tilopay web dashboard and their modules. Different from the assistant's employees (tilopay_list_employees). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `page` | integer | — | Página (por defecto 1) | | `limit` | integer | — | Registros por página (por defecto 20) | | `search` | string | — | Texto a buscar | **Returns** --- # Support and diagnostics > MCP tools to answer with the official guides and test the API connection. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/soporte ## What it covers [#que-cubre] Three read tools: one answers dashboard usage questions with the official guides at tilopay.com/guias, another tests the credentials and one endpoint of each API group to know which part responds when something fails, and the third analyses a code snippet or stack trace you pass it and returns a diagnosis, root cause, fix and risks. That last one does not read repositories and does not run code. ## Tools [#herramientas] ### Tilopay connection diagnostics [#tilopay-diagnostics] - `tilopay_diagnostics` - Access: Read only Verifies the user credentials and tests one endpoint of each group (sales and transactions, catalog, recurring, stored cards). Useful when a tool fails, to know which part of the API is responding. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `environment` | string (production | test) | — | Environment for the transactions test (production by default) | **Returns** `{ checks, failed }` checks is a list with check, ok and detail for each test: credentials, transactions, catalog, recurring and stored cards. failed is the number of failing tests. Credentials come back masked. ### Help from Tilopay guides and tutorials [#tilopay-help-guides] - `tilopay_help_guides` - Access: Read only Answers questions about using the Tilopay dashboard with the official guides at tilopay.com/guias. It returns excerpts and the links to those guides. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `question` | string | yes | The merchant's question, as written | **Returns** `{ articles }` Up to 3 articles with title, url and excerpt, taken from tilopay.com/guias. With no match it returns an empty articles list and a text with the available guides. ### Debug code [#tilopay-debug-code] - `tilopay_debug_code` - Access: Read only Analyses a code snippet or stack trace sent by the user and returns a diagnosis, root cause, a concrete fix and risks. It does not read repositories and does not run code. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | Code snippet, log or stack trace as the user sent it | | `error` | string | — | Error message or observed behaviour, if any | | `language` | string | — | Language or stack (e.g. typescript, php, react). Detected when not given | | `context` | string | — | What the code should do or when it fails | **Returns** `{ report, truncated, redacted, model }` report is the diagnosis in text (diagnosis, root cause, fix and risks). truncated says whether the code was cut down to 12,000 characters; redacted, whether possible credentials were hidden before the analysis; model, the model used. It does not read repositories and does not run the code. --- # Banking tools > MCP tools to read the Tilopay banking API: accounts, balances, payments and statements. Enabled on request and only for users with banking API credentials. - kind: mcp-tool - status: stable - access: write - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario **These tools are not enabled by default.** They are turned on **on request** and only work for users with **Tilopay banking API credentials** registered by Tilopay on the server side. Standard MCP access does not include the group; and if a tool is called without those credentials, it replies that they do not exist. To request it, [contact us](/en/contacto) and state that you need the banking API. ## What it covers [#que-cubre] Nineteen tools over the [banking API](/en/developers/api-bancario): contexts (assignments), credential diagnostics, accounts, balances, full account number, payments, statements, an analytical payments report, validating a destination account, the transfer with its authorization, cancellation and result notice, dual-control approval and closing the banking session. **Thirteen of the nineteen only read information.** `baas_request_statement` generates a read-only document and moves no money. The one tool that sends money is `baas_send_transfer`, and it does not run on its own: the Banca Cliente portal sends an authorization code to your email, and the transfer only goes out when you answer that code and it is confirmed with `baas_confirm_transfer`. If the operation is held under dual control, the approver decides it with `banca_decide_approval` and a code sent to their email. So it cannot run on the model's decision alone. Accounts are identified with `accountType: "IBAN"` plus `accountValue`, dates are RFC 3339 (`2026-01-01T00:00:00Z`) and `assignmentId` is only needed when the user has several contexts. ## Tools [#herramientas] > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - [Other BaaS operations](https://tilopay.com/en/developers/agentes/mcp/api-bancario/otras-operaciones) — `baas_list_assignments`, `baas_diagnostics`, `baas_list_accounts`, `baas_get_balance`, `baas_get_account_number`, `baas_get_statement_status`, `baas_confirm_transfer`, `baas_cancel_transfer`, `banca_decide_approval`, `baas_close_session` (Read only / Read only / Read only / Read only / Read only / Read only / Sensitive / Write / Sensitive / Write): Assignments, credential diagnostics, account listing, balance of one account, full account number, status of a statement request, authorizing and cancelling the pending transfer, dual-control approval and closing the banking session. - [Banking API account balances](https://tilopay.com/en/developers/agentes/mcp/api-bancario/list-balances) — `baas_list_balances` (Read only): Lists the real-time balances of the accessible accounts: account, amounts and cut-off date. - [List banking API payments](https://tilopay.com/en/developers/agentes/mcp/api-bancario/list-payments) — `baas_list_payments` (Read only): Queries (read-only) the movements/payments of one of the merchant's own bank accounts. Indicate the currency (CRC/USD) or the account number from the list: do not write the IBAN. Filters by date, status, currency, method and direction, with cursor pagination. - [Find a payment](https://tilopay.com/en/developers/agentes/mcp/api-bancario/search-payment) — `baas_search_payment` (Read only): Searches for a BaaS payment within an account by exactly one of: paymentId (UUID), publicId (number) or clientReference. Identifiers only from data a tool already returned. - [Payment detail](https://tilopay.com/en/developers/agentes/mcp/api-bancario/get-payment) — `baas_get_payment` (Read only): Gets the detail of a BaaS payment by its internal UUID (read-only). Use only identifiers that another tool returned in this same turn. - [Request an account statement](https://tilopay.com/en/developers/agentes/mcp/api-bancario/request-statement) — `baas_request_statement` (Write): Requests the generation of an account statement for a banking API account, with a 60-day maximum range. It moves no money: it generates a read-only document and returns request_id with status PENDING. - [Banking API payments analyst](https://tilopay.com/en/developers/agentes/mcp/api-bancario/analyze-payments) — `baas_analyze_payments` (Read only): Analyses, read only, the payments of an account in a date range and returns a natural-language report: success, failures, reversals, net amounts and recommendations. It runs a language model over the computed figures. - [Validate BaaS destination account](https://tilopay.com/en/developers/agentes/mcp/api-bancario/validate-destination) — `baas_validate_destination` (Read only): Validates a destination account before transferring: PHONE for SINPE Móvil or IBAN for account transfer. Returns the holder name the bank confirms. - [Send BaaS transfer](https://tilopay.com/en/developers/agentes/mcp/api-bancario/send-transfer) — `baas_send_transfer` (Sensitive): Sends a transfer from the merchant's bank account. Default method: SINPE Móvil (destination by phone). For a transfer to an account use accountType=IBAN. The Banca Cliente portal sends the authorization code to the merchant's email and applies its limits; the merchant answers the code and it is confirmed with baas_confirm_transfer. - [BaaS transfer status](https://tilopay.com/en/developers/agentes/mcp/api-bancario/get-transfer-status) — `baas_get_transfer_status` (Read only): The result of a transfer is not queried: the bank reports it here as soon as it has it (portal event). --- # Banking API account balances > Lists the real-time balances of the accessible accounts: account, amounts and cut-off date. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/list-balances > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_list_balances` - Access: Read only - API operation: `GET /api/public/v1/accounts/balances` — https://tilopay.com/en/developers/api-bancario/cuentas Lists the real-time balances of the accessible accounts: account, amounts and cut-off date. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `accountType` | string | — | Filter by scheme (IBAN) | | `accountValue` | string | — | Filter by identifier | | `limit` | integer | — | Page size, between 1 and 100 | | `offset` | integer | — | Desplazamiento de paginación | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ result }` result = the banking API response as returned. --- # List banking API payments > Reads the payments of an owned account identified by IBAN, with date, status, currency, method and direction filters. Cursor pagination. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/list-payments > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_list_payments` - Access: Read only - API operation: `GET /api/public/v1/transactions/payments` — https://tilopay.com/en/developers/api-bancario/pagos Queries (read-only) the movements/payments of one of the merchant's own bank accounts. Indicate the currency (CRC/USD) or the account number from the list: do not write the IBAN. Filters by date, status, currency, method and direction, with cursor pagination. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `currency` | string | — | ISO 4217 currency (CRC, USD) | | `accountIndex` | integer | — | Número de la cuenta en la lista de cuentas del usuario | | `accountValue` | string | — | Account identifier (IBAN) | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | | `dateFrom` | string | — | From, RFC 3339 (2026-01-01T00:00:00Z) | | `dateTo` | string | — | To, RFC 3339 | | `status` | string (pending | processing | confirmed | posted | failed) | — | Public payment status | | `paymentMethodCode` | string (PIN | SINPE_MOVIL) | — | Payment method | | `direction` | string (OUT | IN) | — | OUT = sent, IN = received | | `clientReference` | string | — | Merchant reference | | `limit` | integer | — | Page size, 100 maximum (20 by default) | | `cursor` | string | — | Cursor from the previous page | **Returns** `{ result }` result = the banking API response as returned, with its pagination cursor. --- # Find a payment > Finds a payment inside an account by exactly one of: paymentId (UUID), publicId (number) or clientReference. Sending more than one returns an error. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/search-payment > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_search_payment` - Access: Read only - API operation: `POST /api/public/v1/transactions/payments/search` — https://tilopay.com/en/developers/api-bancario/pagos Searches for a BaaS payment within an account by exactly one of: paymentId (UUID), publicId (number) or clientReference. Identifiers only from data a tool already returned. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `currency` | string | — | Moneda de la cuenta a consultar (CRC o USD). Forma recomendada. | | `accountIndex` | integer | — | Número de la cuenta en la lista de cuentas del usuario | | `accountValue` | string | — | Account identifier (IBAN) | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | | `paymentId` | string | — | Internal payment UUID | | `publicId` | string | — | Numeric public identifier | | `clientReference` | string | — | Merchant reference | **Returns** `{ result }` result = the banking API response as returned. --- # Payment detail > Gets the detail of a banking API payment from its internal UUID, read only. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/get-payment > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_get_payment` - Access: Read only - API operation: `GET /api/public/v1/transactions/payments/{payment_id}` — https://tilopay.com/en/developers/api-bancario/pagos Gets the detail of a BaaS payment by its internal UUID (read-only). Use only identifiers that another tool returned in this same turn. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `paymentId` | string | yes | Internal payment UUID | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ result }` result = the banking API response as returned. --- # Request an account statement > Requests the generation of an account statement for a banking API account, with a 60-day maximum range. It moves no money: it generates a read-only document and returns request_id with status PENDING. - kind: mcp-tool - status: stable - access: write - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/request-statement > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_request_statement` - Access: Write - API operation: `POST /api/public/v1/accounts/statements` — https://tilopay.com/en/developers/api-bancario/cuentas Requests the generation of an account statement for a banking API account, with a 60-day maximum range. It moves no money: it generates a read-only document and returns request_id with status PENDING. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `currency` | string | — | Moneda de la cuenta (CRC o USD) | | `accountIndex` | integer | — | Número de la cuenta en la lista | | `accountValue` | string | — | Account identifier (IBAN) | | `dateFrom` | string | yes | From, RFC 3339 (2026-01-01T00:00:00Z) | | `dateTo` | string | yes | To, RFC 3339, at most 60 days after dateFrom | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ result }` result = the banking API response with request_id and status. The tool requests the document without email notification. --- # Banking API payments analyst > Analyses, read only, the payments of an account in a date range and returns a natural-language report: success, failures, reversals, net amounts and recommendations. It runs a language model over the computed figures. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/analyze-payments > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_analyze_payments` - Access: Read only Analyses, read only, the payments of an account in a date range and returns a natural-language report: success, failures, reversals, net amounts and recommendations. It runs a language model over the computed figures. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `accountIndex` | integer | — | Número de la cuenta en la lista | | `accountValue` | string | — | Account identifier (IBAN) | | `dateFrom` | string | yes | From, RFC 3339 | | `dateTo` | string | yes | To, RFC 3339 | | `currency` | string | — | ISO 4217 currency | | `direction` | string (OUT | IN) | — | OUT = sent, IN = received | | `question` | string | — | Specific focus for the analysis | | `maxItems` | integer | — | Maximum payments to analyse, between 1 and 1000 (300 by default) | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ summary }` summary carries account, range, totals {count, succeeded, failed, reversed, pending, successRate}, byCurrency with succeededAmount, reversedAmount, netAmount and averageTicket, failureReasons and sample. The written report comes in the text. With no payments in the range it says so and does not analyse. --- # Validate a banking API destination account > Checks a destination account before sending a transfer to it: holder, account type, currency and status. It does not move money. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/validate-destination > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_validate_destination` - Access: Read only - API operation: `POST /api/public/v1/accounts/validate` — https://tilopay.com/en/developers/api-bancario/cuentas Validates a destination account before transferring: PHONE for SINPE Móvil or IBAN for account transfer. Returns the holder name the bank confirms. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `accountType` | string (PHONE | IBAN) | yes | Identifier scheme: PHONE for SINPE Móvil, IBAN for account. | | `accountValue` | string | yes | E.164 phone number or IBAN | | `assignmentId` | string | — | Banking API context (optional) | **Returns** `{ result }` result = data the banking API returns about the destination account (holder, account type, currency and status) before sending a transfer. It does not move money. --- # Send a banking API transfer > Sends a transfer from one of your bank accounts. It validates the destination account first and requires a confirmation code sent to your email. - kind: mcp-tool - status: stable - access: destructive - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/send-transfer **This tool moves real money.** It validates the destination account before sending, and executing the transfer requires a **confirmation code** sent to your email. Without that code the operation is not sent. > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_send_transfer` - Access: Sensitive - API operation: `POST /api/public/v1/transactions/payments` — https://tilopay.com/en/developers/api-bancario/pagos Sends a transfer from the merchant's bank account. Default method: SINPE Móvil (destination by phone). For a transfer to an account use accountType=IBAN. The Banca Cliente portal sends the authorization code to the merchant's email and applies its limits; the merchant answers the code and it is confirmed with baas_confirm_transfer. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `amount` | number | yes | Amount to transfer | | `currency` | string | yes | ISO 4217 currency (CRC, USD) | | `contactName` | string | — | Name of the saved contact (looks up their phone/IBAN) | | `destinationAccountType` | string (PHONE | IBAN) | — | PHONE = SINPE Móvil (default), IBAN = account transfer | | `destinationAccountValue` | string | — | E.164 phone or destination IBAN (if contactName is not used) | | `destinationName` | string | — | Recipient name (if not validated via contact) | | `detailReference` | string | — | Payment description (if omitted, one is generated) | | `assignmentId` | string | — | Banking API context (optional) | | `allowDuplicate` | boolean | — | Solo true cuando el usuario confirmó literalmente que quiere repetir una transferencia idéntica reciente (por ejemplo 'sí, es otra'). Nunca lo pongas por tu cuenta. | **Returns** `{ result: { payment, clientReference, detailReference, destinationName, destinationAccount } }` Sends a transfer from a merchant's BaaS account: payment = the created payment with its payment_id and status; clientReference and detailReference = references for reconciliation; destinationName and destinationAccount = who and which account it was sent to. It validates the destination account beforehand and requires a confirmation code by email. --- # Banking API transfer status > Checks the current status of a transfer by its payment_id: status, result, amount, currency, destination and date. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/get-transfer-status > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. - `baas_get_transfer_status` - Access: Read only - API operation: `GET /api/public/v1/transactions/payments/{payment_id}` — https://tilopay.com/en/developers/api-bancario/pagos The result of a transfer is not queried: the bank reports it here as soon as it has it (portal event). **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `paymentId` | string | — | Internal payment UUID | **Returns** `{ result }` result = current status of the transfer queried by its payment_id (status, result, amount, currency, destination and date). --- # Other BaaS operations > Assignments, credential diagnostics, account listing, balance of one account, full account number, status of a statement request, authorizing and cancelling the pending transfer, dual-control approval and closing the banking session, on a single page. - kind: mcp-tool - status: stable - access: read - last_verified: 2026-10-07 - url: https://tilopay.com/en/developers/agentes/mcp/api-bancario/otras-operaciones > These tools are enabled on request and only work for users with Tilopay banking API credentials. They are not part of the default MCP access: Tilopay enables the group case by case. Requires: Credenciales del API bancario de Tilopay registradas por Tilopay para ese usuario. ### Banking API contexts (assignments) [#baas-list-assignments] - `baas_list_assignments` - Access: Read only Lists the tenant/account contexts (assignments) available for the user's banking API credentials. Use it when another tool asks for assignmentId. **Parameters** No parameters. **Returns** `{ assignments[] }` assignments = available contexts with assignment_id, tenant_code, owner_type, country_code and status. ### Banking API diagnostics [#baas-diagnostics] - `baas_diagnostics` - Access: Read only Checks the user's banking API credentials, the login, the token exchange and access to accounts. Use it when a query fails. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ checks[], failed }` checks = one row per check (credentials, login + assignments, accounts) with ok and detail; failed = how many failed. The credential email comes back masked. ### List banking API accounts [#baas-list-accounts] - `baas_list_accounts` - Access: Read only - API operation: `GET /api/public/v1/accounts` — https://tilopay.com/en/developers/api-bancario/cuentas Lists the accessible banking API accounts, without balances: identifier type, value and currency. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `accountType` | string | — | Filter by scheme (IBAN) | | `accountValue` | string | — | Filter by identifier | | `limit` | integer | — | Page size, between 1 and 100 | | `offset` | integer | — | Desplazamiento de paginación | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ result }` result = the banking API response as returned. ### Balance of one account [#baas-get-balance] - `baas_get_balance` - Access: Read only - API operation: `GET /api/public/v1/accounts/balance` — https://tilopay.com/en/developers/api-bancario/cuentas Gets the current balance of a banking API account identified by IBAN. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `currency` | string | — | Moneda de la cuenta (CRC o USD) | | `accountIndex` | integer | — | Número de la cuenta en la lista | | `accountValue` | string | — | Account identifier (IBAN) | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ result }` result = the banking API response as returned. ### Statement request status [#baas-get-statement-status] - `baas_get_statement_status` - Access: Read only - API operation: `GET /api/public/v1/accounts/statements/{request_id}` — https://tilopay.com/en/developers/api-bancario/cuentas Reads the status of an account statement request. When it is DONE it includes the signed download link and its expiry. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `requestId` | string | yes | request_id returned when the statement was requested | | `assignmentId` | string | — | Banking API context (assignment); only needed when there are several | **Returns** `{ result }` result = the banking API response as returned. ### Merchant's full account number [#baas-get-account-number] - `baas_get_account_number` - Access: Read only Shows the FULL IBAN of one of the merchant's own accounts. Use it ONLY when the merchant explicitly asks for their account number (for example to give it to a customer or supplier) and with confirm=true. It is recorded in the audit log. In any other answer the masked number is used. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `confirm` | boolean | yes | true solo si el comercio pidió explícitamente tu número de cuenta completo | | `currency` | string | — | Moneda de la cuenta (CRC o USD) | | `accountIndex` | integer | — | Número de la cuenta en la lista | | `assignmentId` | string | — | Contexto BaaS (opcional) | **Returns** ### Authorize the pending transfer [#baas-confirm-transfer] - `baas_confirm_transfer` - Access: Sensitive Authorizes the pending transfer with the 6-digit code that arrived AT THE MERCHANT'S EMAIL and was answered in the chat. Never write or repeat the code in the answer. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | yes | Código de 6 dígitos que respondió el comercio | | `intentId` | string | — | Identificador de la intención (si se omite, se usa la pendiente) | **Returns** ### Cancel the pending transfer [#baas-cancel-transfer] - `baas_cancel_transfer` - Access: Write Cancels the transfer that is waiting for the authorization code. Use it when the merchant says they do not want to make it. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `intentId` | string | — | Identificador de la intención (opcional) | **Returns** ### Approve or reject a pending operation [#banca-decide-approval] - `banca_decide_approval` - Access: Sensitive Approves or rejects a transfer held under dual control, with the 6-digit code that arrived AT THE APPROVER'S EMAIL. Never write the code in the answer. **Parameters** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `decision` | string (aprobar | rechazar) | yes | Decisión del aprobador | | `code` | string | yes | Código de 6 dígitos del correo del aprobador | | `approvalId` | string | — | Identificador de la aprobación (si se omite, se usa la pendiente) | | `reason` | string | — | Motivo del rechazo (opcional) | **Returns** ### Close banking session [#baas-close-session] - `baas_close_session` - Access: Write Immediately closes the WhatsApp assistant's active banking session. **Parameters** No parameters. **Returns** --- # Machine-readable documentation > The artifacts the portal publishes for agents: llms.txt, raw Markdown on any URL, openapi.json and mcp.json. - kind: guide - status: stable - last_verified: 2026-08-29 - url: https://tilopay.com/en/developers/agentes/documentacion-legible ## Indexes for models [#llms] - [tilopay.com/llms.txt](https://tilopay.com/llms.txt) — Spanish site index, with the URL and a description of every page. - [tilopay.com/llms-full.txt](https://tilopay.com/llms-full.txt) — the same index with the full content. - [tilopay.com/en/llms.txt](https://tilopay.com/en/llms.txt) and [tilopay.com/en/llms-full.txt](https://tilopay.com/en/llms-full.txt) — the English versions, with `/en` URLs. ## Raw Markdown [#markdown] Any URL on the site returns its Markdown by adding `.md`: for example [tilopay.com/en/developers/sdk/instalacion.md](https://tilopay.com/en/developers/sdk/instalacion.md) or [tilopay.com/en/tarifas.md](https://tilopay.com/en/tarifas.md). It is served as `text/markdown`, with a metadata header (page kind, status, version and last verification date). A URL that does not exist returns 404, not HTML. ## API spec [#openapi] - [`openapi.json`](/developers/openapi.json) — OpenAPI 3.1 spec of the Tilopay API: endpoints, parameters, responses and examples. It is the source of truth for the API. - [`openapi.yaml`](/developers/openapi.yaml) — the same content in YAML. Both routes answer with `Access-Control-Allow-Origin: *`, so they can be read from the browser. ## MCP catalog [#mcp-json] [`mcp.json`](/developers/mcp.json) describes the MCP server: its URL, the access model and the full tool catalog with parameters, output and access level. When a tool wraps an API operation, the entry includes that endpoint. The root-level `verified_at` field carries the date (`YYYY-MM-DD`) on which the catalog was compared against the server. The server version does not change when tools are added, so that is the signal to check whether a cached copy is stale. ---