Introduction
This document describes the API to make mobile top ups and buy mobile packages. This API offers a REST service to:
- Get available balance
- Get top up providers
- Make a mobile top up
- Get top up information
- Get history Top Up
- List available providers to buy mobile packages
- List available mobile packages
- Buy a mobile package
- Get a mobile package purchase status
- Get history purchases
Environments
For publishing of these services Tpaga provides two environments:
- Staging environment:
https://staging.apiv2.tpaga.co/api/gateway_topup/v1/ - Production environment: Tpaga delivers the production URL after the certification of your integration.
Authentication
cURL Example:
CLIENT_ID="neZi59dZrKaUdblk4LvbB391wuzXlzyc65aaBH8r"
CLIENT_SECRET="Ib5m6r0SqLqolkWM7yQ8aBitVRwypDonVcJ4fYn4gD3qIXIX5b683y3gUio9T8Ukau1QEZYvBnNrjoYWC8V0Z5Oag4XXc7P9ny21LD0D8n40fg3JVHH8mJiJf52sPKwh"
AUTH_STRING=$(echo -n "$CLIENT_ID:$CLIENT_SECRET" | base64 | tr -d '\n')
curl -v --location --request POST 'https://staging.apiv2.tpaga.co/o/token/' \
--header "Authorization: Basic $AUTH_STRING" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials'
A successful response returns a JSON object in the following format:
{
"access_token": "<JWT token>",
"token_type": "Bearer",
"expires_in": 3600,
"scope":"read write"
}
An example of a valid request sending the Bearer token:
curl -X GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/merchant/balance' \
--header 'Authorization: Bearer <JWT token>'
Example to revoke an access token:
curl -X POST 'https://staging.apiv2.tpaga.co/o/revoke_token/' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token=<JWT token>' \
--data-urlencode 'client_id=<your-client-id>' \
--data-urlencode 'client_secret=<your-client-secret>'
This API uses OAuth 2.0 with JWT-based access tokens. Clients must first obtain an access token using the client credentials flow and then include it in subsequent API requests via the Authorization header.
Obtaining an Access Token
Endpoint: /o/token/
HTTP Method: POST
Description: To obtain an access token, send a POST request to the /o/token/ endpoint using HTTP Basic Authentication with your client credentials. Your CLIENT_ID and CLIENT_SECRET must be combined, base64-encoded, and included in the Authorization header.
Request Parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| grant_type | The OAuth2 grant type. Must be set to client_credentials. |
String | No |
Note: Client credentials are provided via the HTTP Basic Authentication header.
Response Parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| access_token | The generated JWT token | String | No |
| token_type | The type of token, e.g. "Bearer" | String | No |
| expires_in | The duration (in seconds) for which the token is valid | Integer | No |
HTTP Responses
| Code | Description |
|---|---|
| 200 | The access token was issued successfully. |
| 400 | The request is missing required parameters or includes invalid values. |
| 401 | Client authentication failed. |
Using the Access Token
Include the obtained JWT access token in the Authorization header for subsequent API requests
Token Expiration and Renewal
Access tokens are valid for a limited period (as specified by the expires_in value). We recommend caching your access token and reusing it until it is near expiration. Avoid requesting a new token on every API call. When the token is close to expiring, request a new one to maintain uninterrupted service.
Revoking an Access Token
Endpoint: /o/revoke_token/
HTTP Method: POST
Description: If you need to revoke an access token, send a POST request to the /o/revoke_token/ endpoint.
Request Parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| token | The access token to be revoked. | String | No |
| client_id | The client ID. | String | No |
| client_secret | The client secret. | String | No |
Response Parameters
The response body is empty if the token revocation is successful.
HTTP Responses
| Code | Description |
|---|---|
| 200 | The access token was revoked successfully. The response body will be empty. |
| 400 | The request is missing required parameters or is malformed. |
| 401 | Client authentication failed. |
| 404 | The provided token was not found. |
Environment Considerations
Transaction statuses
Every top up and every mobile package purchase has one of the following statuses. The API always returns them in uppercase.
| Status | Final status | Description |
|---|---|---|
AUTHORIZED |
Yes | The top up or the package purchase was performed successfully. |
REJECTED |
Yes | The top up or the package purchase failed. Check the response body for details. You can send a new request with a new idempotency_token and Tpaga will treat it as a new, different attempt. |
UNKNOWN |
No | There was a communication issue with the provider, so the result is not known yet. |
IN_PROGRESS |
No | The transaction is being processed. For example, you can receive it when you query a transaction after your request timed out. |
CREATED |
No | The transaction was registered, but its processing did not finish because of an internal error. |
The following diagram shows how the statuses change:
Integration guidelines
Follow these guidelines when you buy a top up (POST /topup_account) or a mobile package (POST /package_buy).
The following diagram summarizes how to handle the response of a purchase:
Timeouts
We suggest a timeout of 30 seconds for purchase requests.
Status queries
Query the status of a purchase with Get top up information or Get purchase information when the purchase request timed out, failed with an error, or returned a status that is not final.
- Make at most 3 queries, within a window of up to 15 minutes.
- Wait between queries with exponential backoff, and add a random variation (jitter) of ±20 % to each wait.
- If the status is still not final after the last query, stop querying and wait for the reconciliation process, according to the procedure agreed during your onboarding.
For example, query right after the timeout or the error (minute 0), then at minute 5 and at minute 15. These are waits of 5 and 10 minutes.
Idempotency
The idempotency_token identifies each purchase. Use a value that you never reuse, such as a UUID.
- Top ups: if you send a request with an
idempotency_tokenthat already exists, the API does not create a new transaction. It answers409with the existing transaction: its current status and the data of the original request. Any different data in the new request is ignored. For example, the sameidempotency_tokenwith a different amount does not produce a new top up. - Mobile packages: if you send a purchase with an
idempotency_tokenthat already exists, the API answers422(field: idempotency_token) and does not create a new transaction. Use Get purchase information to query the status of the original purchase. - A new
idempotency_tokenis always processed as a new purchase, even if the rest of the data is the same as in a previous purchase. - After a
422response, correct the data and send the purchase with a newidempotency_token. However, when the422HTTP status is caused by anidempotency_tokenthat already exists, the purchase was already registered, so query its status first. Send a new purchase with a newidempotency_tokenonly if you want an additional purchase or the original one isREJECTED. While the original purchase has a status that is not final, a new purchase (differentidempotency_token) can result in two purchases. - Tpaga does not retry a purchase automatically.
Reversals and cancellations
The API does not offer reversals or cancellations of top ups or mobile package purchases, and there is no channel to request them.
Errors
When a request fails, the response body describes the error.
Validation errors
Example of a validation error (
422):
{
"error_message": "Este campo no puede estar en blanco.",
"field": "payment_token",
"value": ""
}
When the response code is 422, the body has these fields:
| Field | Description | Type | Always present |
|---|---|---|---|
| error_message | Description of the error | String | Yes |
| field | Name of the field that caused the error. It is non_field_errors when the error is not about a specific field, for example when the request body is not a JSON object |
String | Yes |
| value | Value of that field in your request. It is only included when the field is part of the request body | Same type as the value you sent | No |
If several fields are invalid, the response describes only one of them.
Other errors
Example of a
402response:
{
"error_message": "You have no funds. Please top up your account and contact our support team to keep operating. Thank you."
}
Other errors, such as 401, 402 and 403, return a body with only the error_message field.
Example of a
500response:
{
"error_message": "Unknown error",
"error_token": "error-7xk2m9q4w1z8c3v6b0n5h2j4rt"
}
An unexpected error (500) returns error_message with the value Unknown error, and an error_token field.
Top up services
- Get available balance
- Get top up providers
- Make a mobile top up
- Get top up information
- Get history Top Up
- Testing
Get available balance
Endpoint: /merchant/balance
HTTP Method: GET
Description: This service returns the available balance and the maximum loan amount.
Example request:
curl -X GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/merchant/balance' \
--header 'Authorization: Bearer <JWT token>'
Example response:
{
"balance": 0,
"max_loan_amount": 0
}
Request parameters
This endpoint does not need extra parameters to be sent in the request.
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| balance | The current balance that the merchant can use to buy top ups and packages | Integer | no |
| max_loan_amount | The max overdraft amount. | Integer | no |
HTTP responses
| Code | Description |
|---|---|
| 200 | The merchant balance was queried successfully |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 503 | Your account is not properly configured, please contact the support team |
| 5XX | Internal server error, it's safe to retry the request |
Get top up providers
Example Request:
curl -X GET \
https://staging.apiv2.tpaga.co/api/gateway_topup/v1/service_providers \
-H 'Authorization: Bearer <JWT token>'
Example Response:
{
"service_providers": [
{
"slug": "movistar",
"name": "Movistar",
"icon_url": "https://some-icon-url.com"
},
{
"slug": "claro-movil",
"name": "Claro Móvil",
"icon_url": "https://some-icon-url.com"
},
{
"slug": "avantel",
"name": "Avantel",
"icon_url": "https://some-icon-url.com"
},
{
"slug": "uff-movil",
"name": "Uff Móvil",
"icon_url": "https://some-icon-url.com"
},
{
"slug": "etb",
"name": "ETB",
"icon_url": "https://some-icon-url.com"
},
{
"slug": "tigo",
"name": "Tigo",
"icon_url": "https://some-icon-url.com"
}
]
}
Endpoint: /service_providers
HTTP Method: GET
Description: This service returns the current available providers.
Request parameters
This endpoint does not need extra parameters to be sent in the request.
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| service_providers | List of available providers | List | No |
| service_providers -> slug | Unique internal identifier for the provider | String | No |
| service_providers -> name | Human-readable name for the provider | String | No |
| service_providers -> icon_url | Suggested icon for the provider | String | Yes |
Make a mobile top up
Endpoint: /topup_account
HTTP Method: POST
Description: This service tops up the given phone number.
Example request:
curl -X POST \
https://staging.apiv2.tpaga.co/api/gateway_topup/v1/topup_account \
-H 'Authorization: Bearer <JWT token>' \
-H 'Content-Type: application/json' \
-d '{
"idempotency_token": "85ab25a1-6389-4737-b91b-0f7ddbc73e9a",
"amount": 1000,
"service_provider": "etb",
"destination_number": "3110001111",
"payment_token": "wallet-11233dafd",
"payment_origin": "My company"
}'
Example response:
{
"token": "topup-4n8c2x7q1m5z9v3b6k0j2h8wdr",
"idempotency_token": "85ab25a1-6389-4737-b91b-0f7ddbc73e9a",
"destination_number": "3110001111",
"service_provider": "etb",
"amount": 1000,
"payment_token": "wallet-11233dafd",
"payment_origin": "My company",
"route": "PUNTORED",
"external_gateway_auth_code": "526268936",
"gateway_topup_token": "ppr-9t3k7m1x5c8v2b6n0q4z7j1h5w3d",
"status": "AUTHORIZED",
"result_code": 0
}
Request parameters
| Field | Description | Type | Required |
|---|---|---|---|
| destination_number | Mobile phone number, without country code: 10 or 12 digits. For DirecTV, send the DirecTV account number instead: only digits, up to 12. Tpaga completes it with zeros on the left, when necessary. | String | yes |
| service_provider | Service provider slug. Use one of the slugs returned by Get top up providers | String | yes |
| amount | Top up amount in Colombian pesos (COP), as an integer without decimals. It must be a multiple of 100. The minimum and maximum amounts are configured for each client and agreed during onboarding | Integer | yes |
| idempotency_token | A unique identifier for the transaction. See Idempotency | String - max. 128 characters | yes |
| payment_origin | Name of the system where the transaction was generated. Send your company's name here | String - max. 64 characters | yes |
| payment_token | Unique identifier for the transaction in your system | String - max. 255 characters | yes |
If destination_number does not have 10 or 12 digits, the API answers 422 with field: mobile_number. The transaction is registered as REJECTED, so its idempotency_token cannot be used again: send the corrected purchase with a new idempotency_token.
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| token | Internal identifier for the transaction | String | No |
| idempotency_token | Idempotency token received in the request | String | No |
| destination_number | Mobile phone number received in the request | String | No |
| service_provider | Service provider slug | String | No |
| amount | Top up amount | Integer | No |
| payment_token | payment_token received in the request | String | No |
| payment_origin | payment_origin received in the request | String | No |
| status | Status of the transaction. See Transaction statuses for details | String | No |
| result_code | Internal code representing transaction status | Integer | Yes |
HTTP responses
| Code | Description |
|---|---|
| 200 | The request was processed. Check the status field: only AUTHORIZED means that the top up was performed successfully. |
| 401 | Invalid authorization token was provided |
| 402 | Your available funds (balance plus credit limit) are already exhausted. Top up your account to keep operating |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 409 | A top up with the same idempotency_token already exists. The body contains that transaction with its current status. See Idempotency |
| 422 | Invalid parameters. See Validation errors and Idempotency |
| 500 | Unexpected error. Query the top up status with Get top up information. Take into account that sending a top up with a new idempotency_token will be treated as a new, different top up attempt. |
| 502 | The result is unknown (UNKNOWN). It is not a final status: do not treat it as declined. After the status query window, wait for the reconciliation process, according to the procedure agreed during your onboarding |
| 503 (with a transaction body) | The top up was rejected (REJECTED) |
| 503 (without a transaction body) | Your account is not properly configured, please contact the support team |
Error Details
When the response code is 422, the body follows the format described in Validation errors. A repeated idempotency_token is not a validation error in top ups: see Idempotency.
Example Errors
| Field | Description | Example Value | Example Error Message |
|---|---|---|---|
| service_provider | The mobile operator may be disabled or not really exist | invalid-operator | Objeto con slug=invalid-operator no existe. |
| destination_number | They send the field empty or blank | |
Este campo no puede estar en blanco |
| mobile_number | The number does not have 10 or 12 digits. | 09516588 | Este valor no coincide con el patrón requerido. |
| payment_token | They send the field empty or blank. | |
Este campo no puede estar en blanco. |
| payment_origin | They send the field empty or blank. | |
Este campo no puede estar en blanco. |
| amount | The amount is below the minimum configured for your account. | 600 | El monto de la recarga no puede ser menor a 1000.00. |
| amount | The amount exceeds the maximum configured for your account. | 110000 | El monto de la recarga no puede ser mayor a 100000.00. |
| amount | The transaction amount is not divisible by 100. | 6548 | Ensure the given value (6548) is divisible by 100. |
Get top up information
Endpoint: /topup_account/
HTTP Method: GET
Description: This service returns a top up transaction.
Example request:
curl --location --request GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/topup_account/85ab25a1-6389-4737-b91b-0f7ddbc73e9a' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json'
Example response:
{
"token": "topup-4n8c2x7q1m5z9v3b6k0j2h8wdr",
"idempotency_token": "85ab25a1-6389-4737-b91b-0f7ddbc73e9a",
"destination_number": "3110001111",
"service_provider": "etb",
"amount": 1000,
"payment_token": "wallet-11233dafd",
"payment_origin": "My company",
"route": "PUNTORED",
"external_gateway_auth_code": "526268936",
"gateway_topup_token": "ppr-9t3k7m1x5c8v2b6n0q4z7j1h5w3d",
"status": "AUTHORIZED",
"result_code": 0
}
Request parameters
| Field | Description | Type | Required |
|---|---|---|---|
| idempotency_token | The idempotency_token used to create the transaction in the "Make a mobile top up" service |
String | Yes |
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| token | Internal identifier for the transaction | String | No |
| idempotency_token | Idempotency token received in the request | String | No |
| destination_number | Mobile phone number received in the request | String | No |
| service_provider | Service provider slug | String | No |
| amount | Top up amount | Integer | No |
| payment_token | payment_token received in the request | String | No |
| payment_origin | payment_origin received in the request | String | No |
| route | Internal processing route of the transaction | String | No |
| external_gateway_auth_code | Authorization code of the transaction, when it is available | String | Yes |
| gateway_topup_token | Internal reference of the request sent to process the top up, when it is available | String | Yes |
| status | Status of the transaction. See Transaction statuses for details | String | No |
| result_code | Internal code representing transaction status | Integer | Yes |
HTTP responses
| Code | Description |
|---|---|
| 200 | The top up was queried successfully |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 422 | The top up queried by the idempotency_token does not exist. |
| 5XX | Internal server error, it's safe to retry the request |
Get history Top Up
Endpoint: /topup_account/transactions
HTTP Method: GET
Description: This endpoint returns the history of top ups made by a client of the API. It supports paginated queries and date range filtering.
Example request:
curl --location --request GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/topup_account/transactions?page=1&per_page=50&start_date=2026-10-01&end_date=2026-10-31' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json'
Example response:
{
"count": 3,
"transactions": [
{
"idempotency_token": "85ab25a1-6389-4737-b91b-0f7ddbc73e9a",
"destination_number": "3110001111",
"payment_origin": "My company",
"service_provider": "etb",
"amount": 1000,
"status": "AUTHORIZED",
"date_created": "2026-10-29 09:20:29"
},
{
"idempotency_token": "f5bbcc29-bd7e-404f-b732-a99a1eee5dd1",
"destination_number": "3110005555",
"payment_origin": "My company",
"service_provider": "claro-movil",
"amount": 2000,
"status": "REJECTED",
"date_created": "2026-10-29 10:05:12"
},
{
"idempotency_token": "324b2346-c01f-4b41-ab8f-4cc6fe8590de",
"destination_number": "3110002222",
"payment_origin": "My company",
"service_provider": "movistar",
"amount": 5000,
"status": "UNKNOWN",
"date_created": "2026-10-30 14:48:03"
}
]
}
Request parameters
Use page and per_page to paginate the results. Transactions are sorted by creation date, from the oldest to the newest.
Use start_date and end_date to filter by creation date. Both dates are in Colombia time (UTC-5), and both are included: the range goes from 00:00:00 of start_date to the end of end_date. If you send one of them, the other is required. start_date cannot be later than end_date.
| Field | Description | Type | Required | Default |
|---|---|---|---|---|
| page | Page number. Minimum value: 1 | Integer | No | 1 |
| per_page | Number of transactions per page. Minimum value: 1 | Integer | No | 200 |
| start_date | First day of the range, in YYYY-MM-DD format, in Colombia time (UTC-5) |
String | No | |
| end_date | Last day of the range, in YYYY-MM-DD format, in Colombia time (UTC-5) |
String | No |
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| count | Total transactions made by the API Client or within the selected range. | Integer | No |
| transactions | List of current transactions | List | No |
| transactions -> idempotency_token | Idempotency token received in the request | String | No |
| transactions -> destination_number | Mobile phone number received in the request | String | No |
| transactions -> service_provider | Service provider slug | String | No |
| transactions -> amount | Top up amount | Integer | No |
| transactions -> payment_origin | payment_origin received in the request | String | No |
| transactions -> status | Status of the transaction. See Transaction statuses for details | String | No |
| transactions -> date_created | Creation date of the transaction, in Colombia time (UTC-5), in YYYY-MM-DD HH:MM:SS format |
String | No |
HTTP responses
| Code | Description |
|---|---|
| 200 | The list of transactions was queried successfully |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 422 | Invalid parameters sent |
| 5XX | Internal server error, it's safe to retry the request |
Testing
The API offers test phone numbers to assist you in the development and certification process. Send the following phone numbers in your requests to simulate different scenarios. These numbers only work in the staging environment.
| Phone number | Description | HTTP code |
|---|---|---|
| 3110001111 | The top up was performed successfully (AUTHORIZED) |
200 |
| 3110002222 | The result of the top up is unknown (UNKNOWN). It is not a final status: see Transaction statuses |
502 |
| 3110004444 | A request with the same idempotency_token is still being processed. The body contains the transaction in IN_PROGRESS. See Idempotency |
409 |
| 3110005555 | The provider rejected the top up (REJECTED). You can send a new request with a new idempotency_token |
503 |
Mobile packages services
- Get mobile package providers
- Get mobile packages
- Purchase a mobile package
- Get purchase information
- Get history purchases
- Test data
Get mobile package providers
Endpoint: /mobile_package_service_providers_list
HTTP Method: GET
Description: This service returns the mobile package providers that are currently available for purchases. Only enabled providers are listed.
Example request:
curl -X GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/mobile_package_service_providers_list' \
--header 'Authorization: Bearer <JWT token>'
Example response:
{
"service_providers": [
{
"name": "TIGO",
"provider_code": "3",
"operator_logo_url": "https://storage.googleapis.com/bucket-faas/suppliers/3.webp",
"enabled": true
},
{
"name": "CLARO PAQUETES",
"provider_code": "58",
"operator_logo_url": "https://storage.googleapis.com/bucket-faas/suppliers/58.webp",
"enabled": true
},
{
"name": "VIRGIN MOBILE PAQUETES",
"provider_code": "72",
"operator_logo_url": "https://storage.googleapis.com/bucket-faas/suppliers/72.webp",
"enabled": true
},
{
"name": "MOVISTAR PAQUETES",
"provider_code": "30",
"operator_logo_url": "https://storage.googleapis.com/bucket-faas/suppliers/30.webp",
"enabled": true
}
]
}
Request parameters
Use page and per_page to paginate the results. The list has no defined order, so when you paginate, a provider can appear on more than one page or be missing. To get the complete list, request it in a single page, with a per_page value of 200.
| Field | Description | Type | Required | Default |
|---|---|---|---|---|
| page | Page number. Minimum value: 1 | Integer | No | 1 |
| per_page | Number of providers per page. Minimum value: 1 | Integer | No | 200 |
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| service_providers | List of available service providers | List | No |
| service_providers -> provider_code | Unique identifier for the provider on Tpaga's system | String | No |
| service_providers -> name | Name of provider | String | No |
| service_providers -> operator_logo_url | URL of the icon to be displayed on the apps | String | No |
| service_providers -> enabled | Indicates if the service provider is available. | Boolean | No |
HTTP responses
| Code | Description |
|---|---|
| 200 | The service providers were queried successfully |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 422 | Invalid parameters sent |
| 5XX | Internal server error, it's safe to retry the request |
Get mobile packages
Endpoint: /packages
HTTP Method: GET
Description: This endpoint returns the mobile packages currently available for purchase. It can return all the current mobile packages, or only the packages of a specific service provider.
Example request:
curl --location --request GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/packages' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json'
Example request with specific service provider:
curl --location --request GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/packages?provider_identifier=58' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json'
Example response:
{
"packages": [
{
"name": "200 MINUTOS A TODO OPERADOR",
"description": "200 MNTS O 20DIAS",
"amount": 9900,
"validity": "20 DÍAS",
"package_code": "024",
"sku": "50015",
"category_code": "9",
"category_description": "HABLAR",
"provider_code": "58"
},
{
"name": "18 MINUTOS A VENEZUELA - $5.000",
"description": "18MIN_A_VNZLA-30D",
"amount": 5000,
"validity": "30 DÍAS",
"package_code": "353",
"sku": "50050",
"category_code": "9",
"category_description": "HABLAR",
"provider_code": "58"
},
{
"name": "1.5 GB DE NAVEGACION + MUNUTOS Y MENSAJES ILIMI. + REDES SOCIALES (WHATSAPP FACEBOOK TWITTER) + 7 DIAS POR $7.500",
"description": "1.5GB_MIN ILIMT+7D",
"amount": 7500,
"validity": "7 DIAS",
"package_code": "767",
"sku": "50130",
"category_code": "7",
"category_description": "TODO INCLUIDO",
"provider_code": "58"
},
{
"name": "TODO INCLUIDO CON MINUTOS ILIMITADOS CHAT DE WHATSAPP FACEBOOK Y TWITTER 12GB POR 30 DIAS",
"description": "12GB/ILIMITADO_30D",
"amount": 41000,
"validity": "30 DIAS",
"package_code": "939",
"sku": "50148",
"category_code": "7",
"category_description": "TODO INCLUIDO",
"provider_code": "58"
}
]
}
Request parameters
Use provider_identifier to get only the packages of one service provider.
Use page and per_page to paginate the results. Packages are sorted by category, but packages of the same category have no defined order, so when you paginate, a package can appear on more than one page or be missing. To get the complete list, request it in a single page, with a per_page value of 200.
| Field | Description | Type | Required | Default |
|---|---|---|---|---|
| provider_identifier | Service provider identifier. Should be taken from service_providers -> provider_code of the Get mobile package providers service |
String | No | |
| page | Page number. Minimum value: 1 | Integer | No | 1 |
| per_page | Number of packages per page. Minimum value: 1 | Integer | No | 200 |
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| packages | List of current packages | List | No |
| packages -> name | Full package description | String | No |
| packages -> description | Short package description | String | No |
| packages -> amount | Package value | Integer | No |
| packages -> validity | The period of validity of the package - E.g, "30 DÍAS" | String | No |
| packages -> package_code | Unique identifier for the package on Tpaga's system | String | No |
| packages -> sku | A unique identification number | String | Yes |
| packages -> category_code | Identifier of the category from the package | String | No |
| packages -> category_description | Category description of package | String | No |
| packages -> provider_code | Unique identifier for the provider on Tpaga's system | String | No |
HTTP responses
| Code | Description |
|---|---|
| 200 | The list of packages was queried successfully |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 422 | You did not provide valid data for this operation, please check parameters |
| 5XX | Internal server error, it's safe to retry the request |
Purchase a mobile package
Endpoint: /package_buy
HTTP Method: POST
Description: This service allows the client to buy a mobile package.
Example request:
curl --location --request POST 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/package_buy' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"destination_number": "3110001111",
"provider_code": "58",
"amount": 7500,
"idempotency_token": "8aefa051-1383-4d23-bb5c-55d5584746ab",
"payment_token": "wallet-11233dafd",
"package_code": "767"
}'
Example response:
{
"idempotency_token": "8aefa051-1383-4d23-bb5c-55d5584746ab",
"destination_number": "3110001111",
"amount": 7500,
"payment_token": "wallet-11233dafd",
"package_code": "767",
"provider_code": "58",
"route": "PUNTORED",
"external_gateway_auth_code": "526268936",
"status": "AUTHORIZED",
"result_code": 0
}
Request parameters
| Field | Description | Type | Required |
|---|---|---|---|
| idempotency_token | A unique identifier for the transaction. See Idempotency | String - max. 128 characters | Yes |
| amount | Package amount in Colombian pesos (COP), as an integer without decimals. Send the amount of the package, as returned by Get mobile packages. The minimum and maximum amounts are configured for each client and agreed during onboarding |
Integer | Yes |
| provider_code | Service provider identifier. Should be taken from service_providers -> provider_code of the Get mobile package providers service |
String | Yes |
| destination_number | Mobile phone number to apply the package | String | Yes |
| payment_token | Identifier for the payment in your system | String - max. 255 characters | Yes |
| package_code | Package identifier. Should be taken from packages -> package_code of the Get mobile packages service |
String | Yes |
If destination_number is not a valid mobile number, the API answers 422 with field: mobile_number. The transaction is registered as REJECTED, so its idempotency_token cannot be used again: send the corrected purchase with a new idempotency_token.
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| idempotency_token | Idempotency token received in the request to purchase the package | String | No |
| destination_number | Mobile number received in the request to purchase the package | String | No |
| amount | Amount received in the request to purchase the package | Integer | No |
| payment_token | Payment token received in the request to purchase the package | String | No |
| package_code | Package code received in the request to purchase the package | String | No |
| provider_code | Service Provider received in the request to purchase the package | String | No |
| status | Status of the transaction. See Transaction statuses for details | String | No |
| result_code | Internal code representing transaction status | Integer | Yes |
HTTP responses
| Code | Description |
|---|---|
| 200 | The request was processed. Check the status field: only AUTHORIZED means that the package was purchased successfully. |
| 401 | Invalid authorization token was provided |
| 402 | Your available funds (balance plus credit limit) are already exhausted. Top up your account to keep operating |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 409 | A purchase with the same idempotency_token is being processed or was already answered. Query its status with Get purchase information |
| 422 | Invalid parameters, including an idempotency_token that already exists. See Validation errors and Idempotency |
| 500 | Unexpected error. Query the purchase status with Get purchase information. Take into account that sending a purchase with a new idempotency_token will be treated as a new, different purchase attempt. |
| 502 | The result is unknown (UNKNOWN). It is not a final status: do not treat it as declined. After the status query window, wait for the reconciliation process, according to the procedure agreed during your onboarding |
| 503 (with a transaction body) | The purchase was rejected (REJECTED) |
| 503 (without a transaction body) | Your account is not properly configured, please contact the support team |
Error Details
When the response code is 422, the body follows the format described in Validation errors.
Example Errors
| Field | Description | Example Value | Example Error Message |
|---|---|---|---|
| idempotency_token | A package request is being sent with an idempotency that already exists | wb-n001dbfqc60t | Ya existe un/a mobile package transaction con este/a idempotency token. |
| provider_code | The mobile operator does not match the package being sent | 58 | El service_provider se debe corresponder con el mobile_package. |
| provider_code | The mobile operator may be disabled | 58 | El operador móvil del paquete no está habilitado. |
| provider_code | The mobile operator not really exist | 58 | Objeto con provider_code=58 no existe |
| provider_code | The package is disabled | 58 | El paquete móvil no está habilitado. |
| package_code | The package not exist | 767 | Objeto con package_code=767 no existe |
| destination_number | They send the field empty or blank | |
Este campo no puede estar en blanco |
| mobile_number | The number does not have 10 characters long. | 301486565 | Este valor no coincide con el patrón requerido. |
| payment_token | They send the field empty or blank. | |
Este campo no puede estar en blanco. |
| amount | The amount is below the minimum configured for your account. | 600 | El monto del paquete no puede ser menor a 1000.00. |
| amount | The amount exceeds the maximum configured for your account. | 110000 | El monto del paquete no puede ser mayor a 100000.00. |
Get purchase information
Endpoint: /package_buy/
HTTP Method: GET
Description: This service returns a mobile package purchase transaction.
Example request:
curl --location --request GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/package_buy/8aefa051-1383-4d23-bb5c-55d5584746ab' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json'
Example response:
{
"idempotency_token": "8aefa051-1383-4d23-bb5c-55d5584746ab",
"destination_number": "3110001111",
"amount": 7500,
"payment_token": "wallet-11233dafd",
"package_code": "767",
"provider_code": "58",
"route": "PUNTORED",
"external_gateway_auth_code": "526268936",
"status": "AUTHORIZED",
"result_code": 0
}
Request parameters
| Field | Description | Type | Required |
|---|---|---|---|
| idempotency_token | The idempotency_token used to create the transaction in the "Purchase a mobile package" service |
String | Yes |
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| idempotency_token | Idempotency token received in the request to purchase the package | String | No |
| destination_number | Mobile number received in the request to purchase the package | String | No |
| amount | Amount received in the request to purchase the package | Integer | No |
| payment_token | Payment token received in the request to purchase the package | String | No |
| package_code | Package code received in the request to purchase the package | String | No |
| provider_code | Service Provider received in the request to purchase the package | String | No |
| status | Status of the transaction. See Transaction statuses for details | String | No |
| result_code | Internal code representing transaction status | Integer | Yes |
HTTP responses
| Code | Description |
|---|---|
| 200 | The purchase was queried successfully, whatever its status. Check status in the body |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 422 | The purchase queried by the idempotency_token does not exist |
| 5XX | Internal server error, it's safe to retry the request |
Get history purchases
Endpoint: /package_buy/transactions
HTTP Method: GET
Description: This endpoint returns the purchase history of packages made by a client of the API. It supports paginated queries and date range filtering.
Example request:
curl --location --request GET 'https://staging.apiv2.tpaga.co/api/gateway_topup/v1/package_buy/transactions?page=1&per_page=50&start_date=2026-10-01&end_date=2026-10-31' \
--header 'Authorization: Bearer <JWT token>' \
--header 'Content-Type: application/json'
Example response:
{
"count": 3,
"transactions": [
{
"idempotency_token": "8aefa051-1383-4d23-bb5c-55d5584746ab",
"destination_number": "3110001111",
"amount": 7500,
"package_code": "767",
"provider_code": "58",
"status": "AUTHORIZED",
"created_at": "2026-10-29 11:20:02"
},
{
"idempotency_token": "bc2cee0c-b1f2-4033-b02f-cc07fa4d8c00",
"destination_number": "3110000000",
"amount": 9900,
"package_code": "024",
"provider_code": "58",
"status": "REJECTED",
"created_at": "2026-10-29 16:42:37"
},
{
"idempotency_token": "0084ee14-27b8-452c-a886-8f4ceb57270d",
"destination_number": "3110006666",
"amount": 5000,
"package_code": "353",
"provider_code": "58",
"status": "UNKNOWN",
"created_at": "2026-10-30 08:15:44"
}
]
}
Request parameters
Use page and per_page to paginate the results. Transactions are sorted by creation date, from the oldest to the newest.
Use start_date and end_date to filter by creation date. Both dates are in Colombia time (UTC-5), and both are included: the range goes from 00:00:00 of start_date to the end of end_date. If you send one of them, the other is required. start_date cannot be later than end_date.
| Field | Description | Type | Required | Default |
|---|---|---|---|---|
| page | Page number. Minimum value: 1 | Integer | No | 1 |
| per_page | Number of transactions per page. Minimum value: 1 | Integer | No | 200 |
| start_date | First day of the range, in YYYY-MM-DD format, in Colombia time (UTC-5) |
String | No | |
| end_date | Last day of the range, in YYYY-MM-DD format, in Colombia time (UTC-5) |
String | No |
Response parameters
| Field | Description | Type | Nullable |
|---|---|---|---|
| count | Total transactions made by the API Client or within the selected range. | Integer | No |
| transactions | List of current transactions | List | No |
| transactions -> idempotency_token | Idempotency token received in the request to purchase the package | String | No |
| transactions -> destination_number | Mobile number received in the request to purchase the package | String | No |
| transactions -> amount | Amount received in the request to purchase the package | Integer | No |
| transactions -> package_code | Package code received in the request to purchase the package | String | No |
| transactions -> provider_code | Service Provider received in the request to purchase the package | String | No |
| transactions -> status | Status of the transaction. See Transaction statuses for details | String | No |
| transactions -> created_at | Creation date of the transaction, in Colombia time (UTC-5), in YYYY-MM-DD HH:MM:SS format |
String | No |
HTTP responses
| Code | Description |
|---|---|
| 200 | The list of transactions was queried successfully |
| 401 | Invalid authorization token was provided |
| 403 | You do not have enough permissions to perform this action, please contact the support team |
| 422 | Invalid parameters sent |
| 5XX | Internal server error, it's safe to retry the request |
Test data
Because third-party providers sometimes have problems with test environments, the API offers test phone numbers to assist in the development and certification process. These numbers only work in the staging environment.
| Destination Number | Description | HTTP code |
|---|---|---|
| 3110001111 | The mobile package was purchased successfully (AUTHORIZED) |
200 |
| 3110006666 | The result of the purchase is unknown (UNKNOWN). It is not a final status: see Transaction statuses |
502 |
| 3110000000 | The provider rejected the purchase (REJECTED). You can send a new request with a new idempotency_token |
503 |
Changelog
This changelog starts at version 1.1.0. Changes made before that version are not listed.
1.1.0 — 2026-11-06
All the changes in this version are backward compatible. The API itself did not change: this version updates the documentation to describe the current behavior of the API.
Added
- Integration guidelines: suggested timeout, status queries, idempotency, and reversals and cancellations.
- Errors: the format of error responses, common to both services.
- A response example for Make a mobile top up, and query parameters in the examples of both history services.
- Two diagrams: the purchase and status query flow, in Integration guidelines, and the transaction statuses, in Transaction statuses.
- The
pageandper_pageparameters of Get mobile package providers, which were missing in the documentation.
Clarified
- Transaction statuses: a single section for top ups and mobile packages. It lists the statuses in uppercase, as the API returns them, and states which statuses are final.
UNKNOWN,IN_PROGRESSandCREATEDare not final statuses. Do not treat them as declined. After the status query window, wait for the reconciliation process, according to the procedure agreed during your onboarding.- A purchase request that timed out must never be treated as declined or failed.
- A repeated
idempotency_tokenis answered differently by each service: top ups answer409with the existing transaction, and mobile packages answer422. - Use an
idempotency_tokenthat you never reuse, such as a UUID. - A new
idempotency_tokenis always processed as a new purchase, even with the same data. This replaces the statement that the token lets you "retry transactions safely". - The API does not offer reversals or cancellations.
- A
422response describes only one invalid field, and it includesvalueonly when that field is part of the request body. - In top ups,
destination_numbermust have 10 or 12 digits. For DirecTV, it is the DirecTV account number: only digits, up to 12. An invalid number is answered with422andfield: mobile_number, and itsidempotency_tokencannot be used again. - The maximum length of
idempotency_token,payment_originandpayment_token. amountis an integer in Colombian pesos (COP), without decimals. The minimum and maximum amounts are configured for each client. Top up amounts must be multiples of 100.- History services: dates and the
start_dateandend_datefilters are in Colombia time (UTC-5), both days of the range are included, and transactions are sorted from the oldest to the newest. - HTTP responses of Make a mobile top up and Purchase a mobile package:
200means that the request was processed, so checkstatus(onlyAUTHORIZEDmeans success);500means an unexpected error: query the status; a newidempotency_tokenis treated as a new purchase attempt;502meansUNKNOWN;503meansREJECTEDwhen the body contains the transaction, or an account that is not properly configured when it does not. 402is returned only when your available funds (balance plus credit limit) are already exhausted.- Get available balance answers
503when your account is not properly configured. - Get mobile package providers lists only enabled providers, and the list has no defined order.
- Get mobile packages sorts the packages by category. Packages of the same category have no defined order.
provider_codeandpackage_codeare strings in package purchases. The documentation previously declared them as integers. For backward compatibility, the API still accepts integers, but sending strings will be mandatory in an upcoming API version. If you are building a new integration, use strings.
Fixed
- The top up statuses were listed in lowercase (
authorized,rejected,unknown,created,in_progress). The API returns them in uppercase. - The mobile package description of
IN_PROGRESSsaid that this status is returned only when two concurrent requests are received. - The top up error examples listed a
422for anidempotency_tokenthat already exists. Top ups answer409in that case (see Idempotency). error_messageandfieldwere listed as fields of successful responses. They are only part of error responses.- The maximum lengths of
payment_origin(64) andpayment_token(255) were swapped in Make a mobile top up. - In the mobile package error examples, a disabled package is reported in the
provider_codefield, not inpackage_code. result_codewas documented as a non-nullable String in Make a mobile top up. It is a nullable Integer.packages -> provider_codewas documented as Integer in Get mobile packages. It is a String.- In Get history Top Up, the date field is
date_created, notcreated_at. - In both history services, the date format is
YYYY-MM-DD HH:MM:SS, notAAAA-MM-DD HH-MM-SS. - The
500response of the purchase services was described as safe to retry. Retrying with a newidempotency_tokencan result in two purchases. - Get purchase information answers
200for any existing purchase, whatever its status. It does not answer502or503. - Examples: the
idempotency_tokendid not match between the request and the response of Purchase a mobile package, nor between the URL and the body of Get top up information. - The example of Get top up information used
Noneinstead ofnull, included adate_createdfield that this service does not return, and showed an outdatedroutevalue. - The history examples used phone numbers that are not valid, repeated an
idempotency_token, and in mobile packages,countdid not match the number of transactions. - The example request of Purchase a mobile package sent
provider_codeandpackage_codeas numbers. It now sends them as strings. - In the mobile package test data, the HTTP codes of
3110006666(UNKNOWN,502) and3110000000(REJECTED,503) were swapped. - The cURL example to obtain an access token was missing
--request, so curl treatedPOSTas an additional URL. - Typos, the indexes of both services (wrong numbering and broken links), duplicated headings and several descriptions.
- In Get top up information, the descriptions of
route,external_gateway_auth_codeandgateway_topup_tokenreferred to mobile packages.
Removed
- The production URL. Tpaga delivers it after the certification of your integration. The URL did not change, so integrations already in production do not need to do anything.
- The instruction to contact Tpaga's support for
IN_PROGRESS,UNKNOWNandCREATEDtransactions. Instead, wait for the reconciliation process, according to the procedure agreed during your onboarding. payment_originfrom the mobile package error examples. Package purchases do not use this field.- The test value
3110003333(top ups). We suggest not using it in test, QA or certification environments. Tpaga will mark this value as deprecated in a later version and will publish new values to simulate more test scenarios. - The test value
3110007777(mobile packages). We suggest not using it in test, QA or certification environments. Tpaga will mark this value as deprecated in a later version and will publish new values to simulate more test scenarios.