Purchase flow
A purchase uses one universal route: POST /orders/create. The request fields depend on module.
Telegram Stars and Premium
For Stars, send stars_amount (50–100000); for Premium, send months (3, 6 or 12). Both require the recipient's public Telegram username, not their numeric Telegram ID. An initial @ is accepted.
Use /recipient/check with the same username and module parameters before buying. The purchase checks the recipient again.
{
"api_key_id": 1,
"module": "stars",
"stars_amount": 50,
"username": "example_user",
"api_user_id": "customer-789",
"partner_order_id": "order-456"
}
{
"api_key_id": 1,
"module": "tg_premium",
"months": 3,
"username": "example_user",
"api_user_id": "customer-789",
"partner_order_id": "order-456"
}
Do not send nominal_id, quantity or recipient_data for these modules. Order history stores quantity=1; the number of Stars or subscription duration appears in stars_amount or months.
Topups and recipient fields
- Select a product from
/catalog/listwithmodule=topups. - Get its nominals through
/catalog/details. - Call
/catalog/nominalwith bothitem_idandnominal_id. - Use the returned
input_fieldsto collect the customer's details.
Example field:
{
"key": "server",
"label": "Server",
"type": "select",
"required": true,
"options": [
{"key": "eu", "label": "Europe"},
{"key": "asia", "label": "Asia"}
]
}
Use label for display and key for the request. Keys are case-sensitive and can differ between products. Do not hardcode playerId or server for every game.
| Field type | Value sent in recipient_data |
|---|---|
input | Entered text as a string |
select, radio, check | One selected options[].key as a string, not its label or an array |
All values in recipient_data must be strings, including numeric-looking UIDs. Retrieve current fields and server choices when preparing a purchase.
{
"api_key_id": 1,
"module": "topups",
"nominal_id": 11,
"quantity": 1,
"recipient_data": {
"playerId": "123456789",
"server": "eu"
},
"api_user_id": "customer-789",
"partner_order_id": "order-456"
}
This example applies only to a nominal whose fields actually include these keys.
compliance_info is an optional topup-only field for nominals that accept additional payment information. It contains paymentMethod, playerIp and sellerName. Omit it unless required for your integration scenario and accepted for the nominal; an unsupported nominal returns HTTP 400. It does not replace recipient_data. If supplied, playerIp must be the customer's actual IP, not your server's address.
How to interpret recipient checks
HTTP 200 alone does not confirm a recipient.
| Result | Action |
|---|---|
success=true, valid=true | Recipient verified |
success=true, valid=false, supported=false, code=validation_unsupported | Topup verification is unavailable for this product; purchasing is allowed, but the customer's details remain unverified |
valid=false with recipient_not_found, recipient_invalid, invalid_username, invalid_recipient_fields | Correct the recipient details |
recipient_ineligible or premium_already_active | The recipient cannot receive the selected product |
success=false | The check did not complete successfully; inspect code and retryable |
Other returned codes include recipient_fields_unavailable, nominal_unavailable, price_unavailable and recipient_check_unavailable. message is explanatory text; use code for program logic. retryable=true applies to the check, not to replaying a purchase.
Codes
Choose module=codes and a nominal_id from the catalog. Codes require no username, recipient data or recipient check.
{
"api_key_id": 1,
"module": "codes",
"nominal_id": 12,
"quantity": 1,
"api_user_id": "customer-789",
"partner_order_id": "order-456"
}
For topups and codes, quantity defaults to 1. The JSON schema accepts positive integers, but a nominal can impose a smaller quantity limit, and codes are subject to available stock. In particular, some nominals allow at most 10 units per order; exceeding their limit returns HTTP 400.
Price and balance
Catalog prices are your API prices with the platform markup already included. /orders/quote calculates a total but does not reserve stock, check your balance or fix the price.
The purchase recalculates the amount and deducts it from the integration balance. You do not send a price or an acceptable maximum. The saved order's amount_usd is the actual charge.
If the balance is insufficient, the API returns HTTP 400 with detail="Insufficient balance"; no order is created and no amount is charged. A confirmed refusal after charging changes the order to refunded and returns the charged amount.
Order identifiers
api_user_id— your customer's ID, a required string.partner_order_id— optional ID of the order in your system.order_id— AdaptGroup order ID returned by the API.
partner_order_id is not an idempotency key. Sending the same value again creates a new purchase. There is no request_id parameter.
Order statuses
| Status | Meaning |
|---|---|
processing | Purchase accepted; the final result is pending |
completed | Purchase completed successfully |
refunded | Confirmed refusal; the charged amount was returned |
review | The result is not confirmed yet. Funds have not been automatically refunded; do not start another purchase as a retry |
pending | A non-final status accepted by the history filter |
failed | A status accepted by the history filter; do not assume a refund from this value alone |
The current creation flow returns processing, completed, refunded or review. A 200 response from /orders/create uses success=true for processing/completed and success=false for refunded/review.
By contrast, success=true from /orders/status only means that the saved order was found. Inspect order.status.
Lost response or timeout
Do not automatically repeat /orders/create: every call is a separate purchase.
- If you have
order_id, request/orders/status. - If an HTTP
503response containsdetail.order_id, use that ID. - If the response was lost, search
/orders/listby yourpartner_order_idand, if needed,api_user_idand creation period. - An empty list is not proof that an in-flight purchase was never accepted. If the result remains unclear, contact support before buying again.
An example uncertain creation response:
{
"detail": {
"message": "Order creation result unknown; check order status",
"order_id": 125,
"partner_order_id": "order-456"
}
}
Receive the result
Use Order status or configure Webhooks in the dashboard.
For a completed codes order, /orders/status returns order.codes:
[
{"content": "EXAMPLE-CODE", "number": null, "pin": null},
{"content": null, "number": "EXAMPLE-CARD-NUMBER", "pin": "1234"}
]
A code may have content, a number, a PIN or a combination. Optional absent fields in the status response are serialized as null; webhooks may omit them. Other modules and non-completed orders return an empty array.
History lists omit issued codes and recipient data. Telegram notifications also do not include issued codes; they are available through the API and final webhook.