Skip to content

Billing, coupons, and growth

An invoice records a purchase; an order provides package access. Keep payment confirmation and service readiness as separate checks. If another system takes payment, follow the external billing quickstart instead of charging the customer again here.

GoalCreate-invoice fieldsResult after payment
Buy residential trafficpackage_id, integer-byte data, gatewayFunds the recipient’s order and purchased ledger
Buy static proxiesStatic package_id, positive quantity, gateway, and required package-specific selectionsProvisions the purchased static service, subject to inventory
Top up a monetary walletamount in the smallest currency unit, gateway; no package_idCredits money, not proxy traffic
Spend an existing walletPackage purchase fields and gateway: wallet; omit statusDebits the recipient’s wallet and marks the invoice paid on success
Record externally paid servicePackage purchase fields, gateway: manual, status: paidSuperuser-only recording and provisioning; no external card charge

Read package pricing first: a fixed tier may not accept an arbitrary byte amount. A one-time tier limits repeat purchases; it is not synonymous with “never expires.” Unlimited packages can still have an expiration and connection limits. Review package setup and ledger rules.

Use a customer or reseller key with no parent, an enabled gateway, and a test package that accepts 10 GiB. Replace {package_id}, {api_key} and {api_host} with your test values. Store the local purchase record before sending it.

RequestPOST /invoices
curl --request POST 'https://{api_host}/api/v1/invoices' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"package_id": "{package_id}",
"data": 10737418240,
"gateway": "stripe"
}'
Response201 Created

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440004",
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"status": "pending",
"data": 10737418240,
"price_total": 2500,
"payment_url": "https://checkout.example.com/session/example42",
"currency": "USD"
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"country": null,
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"coupon": null,
"payment_amount": 2500,
"payment_currency": "USD",
"fx_market_rate": "1",
"fx_effective_rate": "1",
"fx_markup_percent": "0",
"fx_quoted_at": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z",
"type": "residential",
"is_one_time": false,
"is_payout": false,
"internal_id": "EXAMPLE-00042",
"status": "pending",
"description": "",
"connection_limit": 100,
"quantity": 0,
"data": 10737418240,
"balance": 0,
"price_total": 2500,
"gateway": "stripe",
"payment_url": "https://checkout.example.com/session/example42",
"currency": "USD",
"provider_checkout_id": "example42",
"provider_payment_id": "",
"checkout_status": "ready",
"vat": 0,
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_registration_number": "",
"company_vat_number": "",
"paid": null
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

Expect 201 with a pending invoice and the price calculated by the server. stripe must be enabled and configured for this deployment; consult the gateway catalog, not a hard-coded assumption that every provider is available.

Omit user_id for your own purchase. Sending it requires is_reseller; a reseller can target its own sub-user, and a superuser with reseller privileges can target another account. Sub-users cannot create invoices themselves. A wallet purchase for a target account uses that account’s balance, not an assumed parent wallet.

Send the customer to checkout, then verify

Section titled “Send the customer to checkout, then verify”

Use the returned payment URL, or obtain the documented link with GET /invoices/{id}/pay. Treat provider URLs as sensitive checkout data and only expose the intended customer’s link.

RequestGET /invoices/{invoice_id}
curl 'https://{api_host}/api/v1/invoices/{invoice_id}' \
--header 'Authorization: Static {api_key}'
Response200 OK

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440004",
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"status": "paid",
"data": 10737418240,
"price_total": 2500,
"payment_url": "https://checkout.example.com/session/example42",
"currency": "USD"
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"country": null,
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"coupon": null,
"payment_amount": 2500,
"payment_currency": "USD",
"fx_market_rate": "1",
"fx_effective_rate": "1",
"fx_markup_percent": "0",
"fx_quoted_at": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z",
"type": "residential",
"is_one_time": false,
"is_payout": false,
"internal_id": "EXAMPLE-00042",
"status": "paid",
"description": "",
"connection_limit": 100,
"quantity": 0,
"data": 10737418240,
"balance": 0,
"price_total": 2500,
"gateway": "stripe",
"payment_url": "https://checkout.example.com/session/example42",
"currency": "USD",
"provider_checkout_id": "example42",
"provider_payment_id": "",
"checkout_status": "ready",
"vat": 0,
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_registration_number": "",
"company_vat_number": "",
"paid": "2026-09-16T12:00:00Z"
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

RequestGET /orders
curl 'https://{api_host}/api/v1/orders' \
--get \
--header 'Authorization: Static {api_key}' \
--data-urlencode 'package__id={package_id}'
Response200 OK

Selected fields

{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002"
},
"data": 10737418240,
"data_remaining": 10737418240,
"data_spent": 0,
"ledgers": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"data": 10737418240,
"data_remaining": 10737418240,
"expires": "2026-10-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
]
}
]
}
Show full responseShow selected fields
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"is_auto_renewal": false,
"auto_renewal_percentage": 0,
"auto_renewal_data": 0,
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"proxy_password": "example-proxy-password",
"proxy_password_reset": null,
"pools": [],
"data": 10737418240,
"data_remaining": 10737418240,
"data_spent": 0,
"ledgers": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"data": 10737418240,
"data_remaining": 10737418240,
"expires": "2026-10-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
],
"latest_data_top_up": 10737418240,
"latest_data_top_up_date": "2026-09-16T12:00:00Z",
"data_updated": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
]
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

For another account’s purchase, inspect that customer’s orders instead of the caller’s. Confirm both paid state and usable access. Poll with bounded backoff when payment or fulfillment is still processing; a browser return is not proof of settlement. Do not wait for an invoice.paid accounting webhook: the current sender emits traffic-balance events only.

If checkout initialization returns 502, preserve the returned invoice_id and inspect the existing invoice. After a timeout, follow the recovery guide instead of immediately submitting another invoice.

To top up 25.00 in a two-decimal billing currency, create a separate local purchase record and use this body with the same invoice endpoint:

RequestPOST /invoices
curl --request POST 'https://{api_host}/api/v1/invoices' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"amount": 2500,
"gateway": "stripe"
}'
Response201 Created

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440004",
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"status": "pending",
"data": 0,
"price_total": 2500,
"payment_url": "https://checkout.example.com/session/example42",
"currency": "USD"
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"package": null,
"country": null,
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"coupon": null,
"payment_amount": 2500,
"payment_currency": "USD",
"fx_market_rate": "1",
"fx_effective_rate": "1",
"fx_markup_percent": "0",
"fx_quoted_at": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z",
"type": "balance",
"is_one_time": false,
"is_payout": false,
"internal_id": "EXAMPLE-00042",
"status": "pending",
"description": "",
"connection_limit": 100,
"quantity": 0,
"data": 0,
"balance": 2500,
"price_total": 2500,
"gateway": "stripe",
"payment_url": "https://checkout.example.com/session/example42",
"currency": "USD",
"provider_checkout_id": "example42",
"provider_payment_id": "",
"checkout_status": "ready",
"vat": 0,
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_registration_number": "",
"company_vat_number": "",
"paid": null
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

After it is paid, check GET /profile for the wallet change. To buy 10 GiB with that balance:

RequestPOST /invoices
curl --request POST 'https://{api_host}/api/v1/invoices' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"package_id": "{package_id}",
"data": 10737418240,
"gateway": "wallet"
}'
Response201 Created

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440004",
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"status": "paid",
"data": 10737418240,
"price_total": 2500,
"payment_url": "",
"currency": "USD"
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"country": null,
"user_id": "550e8400-e29b-41d4-a716-446655440010",
"coupon": null,
"payment_amount": 2500,
"payment_currency": "USD",
"fx_market_rate": "1",
"fx_effective_rate": "1",
"fx_markup_percent": "0",
"fx_quoted_at": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z",
"type": "residential",
"is_one_time": false,
"is_payout": false,
"internal_id": "EXAMPLE-00042",
"status": "paid",
"description": "",
"connection_limit": 100,
"quantity": 0,
"data": 10737418240,
"balance": 0,
"price_total": 2500,
"gateway": "wallet",
"payment_url": "",
"currency": "USD",
"provider_checkout_id": "",
"provider_payment_id": "",
"checkout_status": "not_required",
"vat": 0,
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_registration_number": "",
"company_vat_number": "",
"paid": "2026-09-16T12:00:00Z"
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

Omit status: the invoice starts pending and becomes paid after a successful debit. Do not send paid to bypass an insufficient wallet; that value requires a superuser and is not the normal wallet flow. Money units and data bytes are never interchangeable.

GET /invoices lists visible purchases; retrieve a particular invoice to inspect state and use its PDF endpoint to download the billing document. Store every invoice ID even when several purchases share one order. Create, list and detail responses use the full invoice representation, including payment status.

PATCH /orders/{id} accepts auto-renewal threshold and amount settings; send both as zero to disable. These fields do not establish an external subscription, reset a child’s period quota, or prove that a renewal worker is operating. Confirm the deployment’s renewal execution and funding policy before selling automatic renewal.

The public API has no general invoice status PATCH or refund operation. Deleting an invoice is not a payment refund or reversal of its already-provisioned traffic. Handle commercial refunds and access changes as separate, authorized actions; never subtract shared quota from an independently purchased order.

Preview a discount before creating the purchase:

RequestPOST /coupons/calculate-price
curl --request POST 'https://{api_host}/api/v1/coupons/calculate-price' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"package_id": "{package_id}",
"data": 10737418240,
"coupon_code": "WELCOME20"
}'
Response200 OK
{
"price_original": 2500,
"price_discounted": 2000,
"discount_percentage": 20
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

The response provides price_original, price_discounted and discount_percentage. This calculation does not create or pay an invoice; include coupon_code in the subsequent purchase and use the server’s final price. Eligibility can change between preview and checkout.

List/retrieve expose visible coupons. Staff accounts can create, update (PUT or PATCH) and delete coupons; use conditional writes where supported. The redeems operation reports visible redemption records. Keep limits, expiry, package eligibility and redemption counts separate from a browser-calculated discount.

GET /rewards shows claimable reward state. To claim an eligible data reward, POST /rewards/claim accepts:

{ "type": "data", "description": "Transfer referral data reward" }

A successful claim returns 201 with no response body. Re-read reward and balance-bearing resources afterward. Check type-specific thresholds and eligibility; not every reward is data. If the result is uncertain, inspect the reward and balance before repeating the claim.

Affiliate accounts can use GET /affiliates for attributed customers, /affiliates/rewards for individual rewards, and /affiliates/rewards/overall for time-series earnings. Supply explicit reporting dates and timezone where supported. These views support an affiliate dashboard; they do not replace your own payout ledger.