Introduction
This document describes the API used to receive payments from the Tpaga Wallet, using OTP (One Time Password) payment codes.
Depending on the use case you are trying to implement, you have two different options to process payments using OTP payments: (please check with our team before deciding which option to use. We will help based on your particular case).
- Immediate capture-settle. (In one step).
- Authorize first then settle. This option is mostly used when you want to authorize a charge first (maybe because you don't know the final amount yet) and then, after you deliver your product/service, you can settle the final amount.
Before calling /authorize or /capture, call POST /info to obtain the OTP amount and related data. /info does not consume the OTP.
1. Immediate capture-settle
Overview of the payment process
The sequence of operations required to receive a payment from a Tpaga Wallet user is as follows:
- The wallet user (your customer) shows you an OTP payment code; you can scan it as a QR code, or type it in manually in your system/POS.
- Your backend system performs a request to our API (
/capture), asking it to charge the amount you indicate, and pay it using the OTP code you received from the Wallet user. - Tpaga charges the Wallet user, and reports back to you, and to the Wallet user, about the purchase.
The sequence of events is shown in the diagram below:
2. Authorize first then settle
It is possible to receive a payment by authorizing it, and then settling it (these two operations together have the same effect as capturing the OTP as explained before). Receiving a payment in this fashion allows you to check earlier in the transaction that a given OTP actually has enough funds to cover the cost of the purchase.
The sequence of operations to receive a payment in this fashion is the following:
- The wallet user (your customer) shows you an OTP payment code; you can scan it as a QR code, or type it in manually in your system/POS.
- Your backend system performs a request to our API (
/authorize), asking it to authorize the amount you indicate, using the OTP code you received from the Wallet user. - The Tpaga API answers your backend telling it whether the OTP has enough money to back it or not, and the OTP data.
- If the authorization was successful, you can deliver the product/service, and settle the transaction by consuming the
/settleendpoint. - The Tpaga API answers back to your backend system with the purchase information, and notifies the Wallet user.
The sequence of events is shown in the diagram below:
Requirements
- A merchant API key for staging and/or production (provided by Tpaga)
- Access to the OTP Payments API endpoints already used by your integration
- For cashout (cash withdrawal) from August 2026: collect
TipoDocandDocumentoat the kiosk (see Cashout: receiver identification for more details)
Environments
We have two independent environments for development and production applications.
- Staging:
https://stag.wallet.tpaga.co/api/v1/otp - Production:
https://wallet.tpaga.co/api/v1/otp
Authentication
To use this API you must have an API key, which Tpaga will provide you, both for our staging (sandbox) and production environments.
Once you have it, you can authenticate your requests by adding the Authorization HTTP header, and passing the API key as the value for that header.
Example: if your key is mak-1234512345, send:
Authorization: mak-1234512345
Example request with API key:
curl -X POST 'https://stag.wallet.tpaga.co/api/v1/otp/info' \
--header 'Authorization: mak-1234512345' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_code": "1234567",
"additional_data": {
"TipoDoc": "CC",
"Documento": "1019053548"
}
}'
Cashout: receiver identification
For cashout (cash withdrawal) OTPs, from August 2026 the person at the kiosk must be the same wallet user who created the OTP. Send TipoDoc and Documento so Tpaga can verify they match the OTP owner.
Required fields in additional_data
You must send inside additional_data:
| Field | Required | Rule |
|---|---|---|
TipoDoc |
Yes (cashout from August 2026) | Document type of the receiver. Accepted values: CC, CE, PA. Must match the OTP owner (case-insensitive). |
Documento |
Yes (cashout from August 2026) | Exact document number of the OTP owner. Leading zeros are not normalized (0123 ≠ 123). |
Example
additional_data:
{
"TipoDoc": "CC",
"Documento": "1019053548",
"Nombres": "ANA",
"Apellidos": "PEREZ"
}
Where it applies
| Endpoint | Validates TipoDoc / Documento |
|---|---|
POST /info |
Yes (from August 2026; check Cashout: receiver identification for more details) |
POST /authorize |
Yes (from August 2026; check Cashout: receiver identification for more details) |
POST /capture |
Yes (from August 2026; same rules as authorize) |
POST /settle |
No — requires a prior successful /authorize or /capture with TipoDoc and Documento |
Recommended cashout flow
- User creates cashout OTP in app
- Merchant
POST /info(+TipoDoc,Documento) - Merchant
POST /authorize(+TipoDoc,Documento) - Merchant delivers cash
- Merchant
POST /settle(authorization_code+ amount + order_id)
Alternative: POST /capture with TipoDoc and Documento in one call.
Validation errors (cashout from August 2026)
Example
HTTP 422:
{
"error_code": 422,
"error_message": "Se requiere TipoDoc y Documento en additional_data",
"field_name": "additional_data",
"rejected_value": {}
}
| Situation | HTTP | error_message |
|---|---|---|
Missing TipoDoc and/or Documento |
422 | Se requiere TipoDoc y Documento en additional_data |
| Document does not match OTP owner | 422 | La identificación del receptor no coincide con la del titular del código |
| OTP owner has no identification in wallet | 422 | No se puede procesar el retiro en este momento. Por favor indique al usuario que se comunique con soporte de Tpaga |
| Settle without prior authorize/capture with validated ID | 403 | La liquidación requiere autorización previa con identificación del receptor validada |
Example
HTTP 403on/settle:
{
"error_code": 403,
"error_message": "La liquidación requiere autorización previa con identificación del receptor validada",
"additional_data": {
"authorization_code": "<token>"
}
}
Services
Get code information (/info)
Endpoint: /info
HTTP Method: POST
Description: Returns information about an OTP without consuming it. Safe to call multiple times. Guarantees existence of the code, not availability of funds. Call this endpoint to obtain the OTP amount before /authorize or /capture.
For cashout from August 2026, send TipoDoc and Documento to validate the receiver (check Cashout: receiver identification for more details). Failure returns 422.
Example request:
curl -X POST 'https://stag.wallet.tpaga.co/api/v1/otp/info' \
--header 'Authorization: <merchant_api_key>' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_code": "1234567",
"additional_data": {
"TipoDoc": "CC",
"Documento": "1019053548"
}
}'
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
| payment_code | String | Yes | OTP shown by the wallet user |
| additional_data | Object | Conditional | For cashout from August 2026: must include TipoDoc and Documento (check Cashout: receiver identification for more details) |
HTTP responses
| Code | Description |
|---|---|
| 200 | Code information returned |
| 401 | Missing or invalid API key |
| 404 | Code not found or expired |
| 422 | Validation error (including receiver identification for cashout from August 2026) |
| 503 | Temporary failure; safe to retry |
Authorize (/authorize)
Endpoint: /authorize
HTTP Method: POST
Description: Reserves the OTP amount for the merchant. Completes the purchase later with /settle.
For cashout from August 2026, additional_data must include matching TipoDoc and Documento (check Cashout: receiver identification for more details). On failure returns 422 and does not authorize.
Example request:
curl -X POST 'https://stag.wallet.tpaga.co/api/v1/otp/authorize' \
--header 'Authorization: <merchant_api_key>' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_code": "1234567",
"purchase_amount": 50000,
"purchase_order_id": "ORDER-123456789",
"additional_data": {
"TipoDoc": "CC",
"Documento": "1019053548"
}
}'
HTTP responses
| Code | Description |
|---|---|
| 200 | Authorized; returns authorization_code |
| 401 | Missing or invalid API key |
| 402 | Insufficient funds |
| 404 | Code not found, expired, or already used |
| 422 | Validation error (including TipoDoc/Documento) |
| 502 / 503 | Upstream or temporary failure |
Settle (/settle)
Endpoint: /settle
HTTP Method: POST
Description: Finalizes a previously authorized OTP. Amount must be ≤ authorized amount.
For cashout from August 2026, /settle does not ask again for TipoDoc / Documento. It requires a prior successful /authorize (or /capture) with those fields. Otherwise returns 403 (check Cashout: receiver identification for more details).
Example request:
curl -X POST 'https://stag.wallet.tpaga.co/api/v1/otp/settle' \
--header 'Authorization: <merchant_api_key>' \
--header 'Content-Type: application/json' \
--data-raw '{
"authorization_code": "<token_from_authorize>",
"purchase_amount": 50000,
"purchase_order_id": "ORDER-123456789"
}'
HTTP responses
| Code | Description |
|---|---|
| 200 | Settled |
| 401 | Missing or invalid API key |
| 403 | Cashout from August 2026: missing prior validated receiver identification |
| 404 | Authorization code not found or expired |
| 422 | Validation error |
| 503 | Temporary failure; safe to retry |
Capture (/capture)
Endpoint: /capture
HTTP Method: POST
Description: Authorize + settle in a single request.
For cashout from August 2026, same TipoDoc / Documento rules as /authorize (check Cashout: receiver identification for more details).
Example request:
curl -X POST 'https://stag.wallet.tpaga.co/api/v1/otp/capture' \
--header 'Authorization: <merchant_api_key>' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_code": "1234567",
"purchase_amount": 50000,
"purchase_order_id": "ORDER-123456789",
"additional_data": {
"TipoDoc": "CC",
"Documento": "1019053548"
}
}'
HTTP responses
Same family as /authorize (including 422 for receiver identification failures).
Cancel (/cancel)
Endpoint: /cancel
HTTP Method: POST
Description: Voids a payment and returns funds to the wallet user when applicable. Allowed within 24 hours of creation.
Revert (/revert)
Endpoint: /revert
HTTP Method: POST
Description: Same effect as cancel, but identified by purchase_order_id when you do not have the authorization_code.
About OTP codes
- Generated by the wallet user in the app
- Must be used within 3 minutes of creation (before authorize/capture)
- Format: digits with Luhn check digit
- Existence of a code does not guarantee funds are available; use
/authorizeor/captureto reserve them - A user can have only one active payment code at a time
Errors
| Error Code | Meaning |
|---|---|
| 400 | Bad Request -- Your request is invalid. |
| 401 | Unauthorized -- Your API key is wrong. |
| 402 | Payment Required -- Insufficient funds. |
| 403 | Forbidden -- Not allowed (e.g. settle without validated receiver ID on migrated cashout). |
| 404 | Not Found -- The specified resource could not be found. |
| 409 | Conflict -- The OTP was already authorized/canceled/settled. |
| 422 | Unprocessable Entity -- Validation error (including TipoDoc/Documento on migrated cashout). |
| 502 | Bad Gateway -- Upstream payment system failure. |
| 503 | Service Unavailable -- Temporary failure; safe to retry. |
| 500 | Internal Server Error -- We had a problem with our server. Try again later. |