Orders API
Endpoints for creating, managing, and renewing proxy orders including IP provisioning and traffic top-ups.

For authentication, error format, and common headers, see General API Info.
Orders
Get Proxy Catalog Config
GET /orders/config
Get the full proxy catalog config. Public β no auth required.
Response
{
"proxyTypes": [
{
"id": "residential-giga",
"name": "Residential Giga",
"slug": "residential-giga",
"shortDescription": "Real residential IPs, pay per Giga β maximum trust.",
"paymentModel": "prepaid",
"unit": "giga",
"pricePerUnit": 1.5,
"hasTraffic": true,
"trafficDiscounts": [
{ "min": 10, "discount": 0.05, "label": "5% off" }
],
"countDiscounts": [],
"tags": ["Rotating IP", "Real ISP"],
"countryMode": "anytime",
"requiresCountry": false,
"supportsCountry": true,
"supportsCity": true,
"supportsState": true,
"supportsChangeCountry": true,
"billingPeriods": [],
"presets": [
{ "label": "Trial", "trafficGb": 1 }
]
},
...
],
"billingPeriods": [
{ "id": "week", "label": "Weekly", "multiplier": 0.4167 },
{ "id": "month", "label": "Monthly", "multiplier": 1 },
{ "id": "year", "label": "Yearly", "multiplier": 10.5 }
],
"minOrderAmount": 0.5,
"trafficPricePerGb": 0.10,
"trafficVolumeDiscounts": [
{ "min": 50, "discount": 0.05, "label": "5% off traffic" },
{ "min": 100, "discount": 0.10, "label": "10% off traffic" },
{ "min": 500, "discount": 0.15, "label": "15% off traffic" }
]
}Preview Order Pricing Auth required
POST /orders/preview
Preview order pricing before buying.
Request Body
Prepaid giga:
{
"proxyTypeId": "residential-giga",
"trafficGb": 50
}Postpaid IP:
{
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"billingPeriod": "month",
"countries": { "US": 10, "DE": 10, "GB": 5 }
}| Name | Type | Description | Required |
|---|---|---|---|
proxyTypeId | string | Proxy type ID from config | Yes |
trafficGb | number | GB of traffic | Giga types: Yes |
count | number | Number of IPs | IP types: Yes |
billingPeriod | string | week / month / year | No |
countries | object | Country β count map | No |
Response
{
"proxyType": {
"id": "residential-giga",
"name": "Residential Giga",
"paymentModel": "prepaid",
"unit": "giga"
},
"breakdown": {
"proxyTypeId": "residential-giga",
"count": 0,
"trafficGb": 50,
"subtotal": 63.75,
"discountAmount": 11.25,
"total": 63.75
},
"display": {
"totalDisplay": 2637.19,
"currency": "UAH",
"exchangeRate": 41.37
}
}Create Order Auth required
POST /orders/create
Create a real order. Both prepaid and postpaid orders deduct balance immediately.
Request Body
Postpaid with selected countries:
{
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"billingPeriod": "month",
"countries": { "US": 10, "DE": 10, "GB": 5 }
}Postpaid with random countries:
{
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"billingPeriod": "month"
}When
countriesis omitted ornull, IPs are assigned from random available countries. Country selection does not affect price.
Response
Full order object (same as GET /orders/:id).
Errors
{ "message": "order.invalid_proxy_type" }
{ "message": "order.invalid_billing_period" }
{ "message": "order.invalid_count" }
{ "message": "order.invalid_traffic_gb" }
{ "message": "order.countries_required" }
{ "message": "order.insufficient_balance" }Get Orders Summary Auth required
GET /orders/summary
Lightweight summary β single aggregation query, no pagination. Returns order counts, total IPs, traffic purchased, and amount spent β grouped by status and proxy type. Ideal for dashboard badges, spending overviews and per-section counters.
Response
{
"total": {
"orders": 7,
"ips": 35,
"trafficGb": 120.5,
"spentUsd": 89.40
},
"byStatus": {
"pending": { "orders": 1, "ips": 5, "trafficGb": 10.0, "spentUsd": 9.00 },
"paid": { "orders": 0, "ips": 0, "trafficGb": 0, "spentUsd": 0 },
"active": { "orders": 4, "ips": 20, "trafficGb": 100.5, "spentUsd": 68.40 },
"expired": { "orders": 1, "ips": 5, "trafficGb": 10.0, "spentUsd": 9.00 },
"cancelled": { "orders": 1, "ips": 5, "trafficGb": 0, "spentUsd": 3.00 },
"refunded": { "orders": 0, "ips": 0, "trafficGb": 0, "spentUsd": 0 }
},
"byType": {
"private-proxy": {
"orders": 3, "ips": 15, "trafficGb": 0, "spentUsd": 27.00,
"byStatus": {
"active": { "orders": 2, "ips": 10, "trafficGb": 0, "spentUsd": 18.00 },
"expired": { "orders": 1, "ips": 5, "trafficGb": 0, "spentUsd": 9.00 }
}
},
"server-static": {
"orders": 2, "ips": 20, "trafficGb": 20.5, "spentUsd": 36.40,
"byStatus": {
"active": { "orders": 2, "ips": 20, "trafficGb": 20.5, "spentUsd": 36.40 }
}
},
"residential-giga": {
"orders": 2, "ips": 0, "trafficGb": 100.0, "spentUsd": 26.00,
"byStatus": {
"active": { "orders": 1, "ips": 0, "trafficGb": 50.0, "spentUsd": 13.00 },
"pending": { "orders": 1, "ips": 0, "trafficGb": 50.0, "spentUsd": 13.00 }
}
}
}
}Aggregate Object Fields
| Name | Type | Description | Required |
|---|---|---|---|
orders | number | Number of orders | β |
ips | number | Total IPs purchased (count column sum) | β |
trafficGb | number | Total traffic purchased in GB | β |
spentUsd | number | Total amount spent in USD | β |
byStatusalways includes all 6 statuses (zeros for statuses with no orders).byType.*.byStatusonly includes statuses that have at least one order for that type.
List Orders Auth required
GET /orders
List user's orders (paginated).
Query Parameters
| Name | Type | Description | Required |
|---|---|---|---|
page | number | Page number (default: 1) | No |
limit | number | Items per page (default: 20) | No |
type | string | Filter by proxy type ID | No |
Response
{
"orders": [ ... ],
"total": 42,
"page": 1,
"pages": 3
}Get Order by ID Auth required
GET /orders/:id
Get a single order by ID.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Response
{
"id": "80-472109",
"name": "My US Proxies",
"status": "active",
"proxyType": {
"id": "server-static",
"name": "Server Static",
"paymentModel": "postpaid",
"unit": "ip"
},
"count": 5,
"trafficGb": 10,
"billingPeriod": "month",
"proxyUsername": "u_a1b2c3",
"proxyPassword": "xYz9q2Wp4rT8mN1k",
"peer": {
"host": "us1.fonproxy.com",
"portMin": 10000,
"portMax": 19999
},
"countries": { "US": 3, "DE": 2 },
"usage": { "totalGb": 1.2, "maxGb": 10, "usedPercent": 12.0, "remainingGb": 8.8 },
"provisioning": {
"state": "ok",
"assignedCountries": null,
"missingCountries": null,
"assignedCount": 5,
"missingCount": 0
},
"pricing": { "totalUsd": 4.50 },
"dates": { "createdAt": "2026-04-01T10:00:00.000Z", "activatedAt": "2026-04-01T10:00:05.000Z" }
}Order statuses: pending, paid, active, expired, cancelled, refunded
Provisioning Field
Every order response includes a provisioning object that surfaces IP assignment state β the frontend never needs to parse raw meta.
| Name | Type | Description | Required |
|---|---|---|---|
state | "ok" | "pending" | "partial" | Current provisioning state | β |
assignedCountries | Record<string,number> | null | Countries assigned, with count per country | β |
missingCountries | Record<string,number> | null | Countries still missing IPs, with count needed | β |
assignedCount | number | Total IPs successfully assigned | β |
missingCount | number | Total IPs still missing | β |
State meanings:
| State | When | What to show |
|---|---|---|
"ok" | Fully provisioned (or not applicable) | Nothing β order is healthy |
"pending" | Postpaid order paid but waiting for IPs. Not yet running. | Banner: "Your order is being set upβ¦" with missingCountries breakdown |
"partial" | Prepaid order active but some countries had insufficient IPs. Running with partial IPs. | Warning: "Order active with partial IPs" + missingCountries |
Example β pending (postpaid, waiting for DE stock):
{
"state": "pending",
"assignedCountries": { "US": 3, "CA": 2 },
"missingCountries": { "DE": 1 },
"assignedCount": 5,
"missingCount": 1
}Example β partial (prepaid, active but missing some):
{
"state": "partial",
"assignedCountries": { "US": 10 },
"missingCountries": { "FR": 3, "IT": 2 },
"assignedCount": 10,
"missingCount": 5
}The
statefield is enough for the frontend to decide what to render.
Usage Fields
| Name | Type | Description | Required |
|---|---|---|---|
uploadBytes | number | Total bytes uploaded | β |
downloadBytes | number | Total bytes downloaded | β |
totalBytes | number | Upload + download | β |
requestCount | number | Total proxy requests processed | β |
uploadGb | number | Upload in GB (3 decimal places) | β |
downloadGb | number | Download in GB (3 decimal places) | β |
totalGb | number | Total traffic in GB | β |
maxBytes | number | null | Max allowed bytes (giga orders only, null for IP orders) | β |
maxGb | number | null | Max allowed GB (order.trafficGb for giga, null for IP) | β |
remainingBytes | number | null | Remaining bytes before quota is exhausted | β |
remainingGb | number | null | Remaining GB | β |
usedPercent | number | null | Percentage of quota used (0β100, 2 decimal places) | β |
lastReportedAt | string | null | ISO timestamp of last peer usage report | β |
For giga orders (
unit: 'giga'):maxGbequals the purchasedtrafficGb. Frontend can show a progress bar usingusedPercent. For IP orders (unit: 'ip'):maxBytes,maxGb,remainingBytes,remainingGb,usedPercentare allnullβ traffic is informational only.
Automatic IP Provisioning
After an order is created and paid, IP assignment happens automatically via an event-driven background process:
| Payment model | Behaviour |
|---|---|
| prepaid | Balance deducted immediately. IPs auto-assigned by country. Order always activated β even if some countries had insufficient IPs (partial). provisioning.state = "partial" with missingCountries breakdown. |
| postpaid | Order stays pending until all IPs assigned. If all countries filled β activates immediately (state: "ok"). If any country is short β stays pending (state: "pending"). Admin fills missing slots; order auto-activates. |
Frontend handling by provisioning.state:
| State | Status | What to show |
|---|---|---|
"ok" | active | Normal order view |
"pending" | pending | Full-page "Setting up your orderβ¦" banner with missingCountries table. Order not running. |
"partial" | active | Warning banner "Running with partial IPs β some locations pending" + missingCountries table. IPs are working. |
Admins can use
POST /admin/orders/:id/ips/assign-by-countryorPOST /admin/orders/:id/ips/add-by-countryto fill missing slots. Once all missing slots are filled, the postpaid order auto-activates.
Get Order IPs Auth required
GET /orders/:id/ips
Get the IPs assigned to an order.
- Only active (assigned) and visible IPs are returned β IPs hidden by admin (
visible=false) are excluded. - The
indexfield is the absolute position (0-based). There may be gaps where hidden IPs sit β this is intentional. - Connection info is computed from the active peer:
host : portMin + index. - Credentials are the order's own
proxyUsername/proxyPasswordβ NOT the original proxy creds.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Response
{
"orderId": "K4Xb9r",
"credentials": {
"username": "user_a1b2c3d4e5f6",
"password": "xYz9q2Wp4rT8mN1k"
},
"ips": [
{
"index": 0,
"host": "us1.fonproxy.com",
"port": 10000,
"originalIp": "185.123.45.67",
"countryCode": { "geoip": "US" },
"city": { "geoip": "New York" },
"state": { "geoip": "NY" },
"status": "active",
"lastVerifiedAt": "2026-03-23T08:14:00.000Z",
"assignedAt": "2026-03-20T10:01:00.000Z"
},
...
],
"total": 2,
"provisioning": {
"state": "ok",
"assignedCountries": null,
"missingCountries": null,
"assignedCount": 2,
"missingCount": 0
}
}Index
1is missing β that IP was hidden by admin. The user sees 0, 2 with ports 10000, 10002.Use
credentials.username/credentials.passwordto authenticate withhost:port.If no peer is active,
hostandportwill benull.For pending orders (
provisioning.state === "pending"),ipswill be empty or partial andprovisioning.missingCountriesshows what's still being allocated. Show a "waiting for IPs" banner.For partial orders (
provisioning.state === "partial"),ipscontains the assigned IPs andprovisioning.missingCountriesshows what's still missing. The order is running β show a warning.
Reset Public Download Hash Auth required
POST /orders/:id/reset-hash
Reset the public download hash for an order.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Response
{ "publicHash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" }Update Order Settings Auth required
PATCH /orders/:id/settings
Update user-editable order settings. All fields optional.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Request Body
| Name | Type | Description | Required |
|---|---|---|---|
name | string | null | Custom label. Pass null to clear. Falls back to order ID in responses. | No |
proxyUsername | string | New proxy username. Letters and digits only. Stored as u_<value>. | No |
proxyPassword | string | New proxy password. Cannot be empty. | No |
{
"name": "My US Proxies",
"proxyUsername": "myuser",
"proxyPassword": "mySecurePass123"
}Response
Full order object (same as GET /orders/:id).
Errors
{ "message": "order.username_empty" }
{ "message": "order.password_empty" }Delete Order Auth required
DELETE /orders/:id
Soft-delete an order. Allowed for all order statuses β pending, paid, active, expired, cancelled, refunded.
Cascades soft-delete to related order_ips, order_usage, and order_renewal_configs. Soft-deleted orders are retained for 30 days before hard-deletion.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Response
{ "deleted": true }Errors
{ "message": "order.not_found" }Order Renewal & Traffic Management
Get Order Pricing Options Auth required
GET /orders/:id/pricing
Get all pricing options for renewal and traffic top-up on an existing order.
Returns:
- Renewal (postpaid only): full price breakdown for renewing the current billing period.
- Traffic top-up (all types): selectable preset options with full breakdown.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Response (postpaid order)
{
"orderId": "80-472109",
"proxyTypeId": "private-proxy",
"proxyTypeName": "Private Proxy",
"paymentModel": "postpaid",
"unit": "ip",
"renewal": {
"billingPeriod": "month",
"breakdown": {
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"period": "month",
"periodLabel": "Monthly",
"subtotal": 21.38,
"trafficCost": 2.50,
"discount": { "min": 25, "discount": 0.05, "label": "5% off" },
"discountAmount": 1.13,
"total": 23.88
},
"discountPercent": 0,
"discountAmount": 0,
"total": 23.88,
"canRenew": true
},
"trafficTopup": {
"options": [
{
"trafficGb": 5,
"pricePerGb": 0.05,
"subtotal": 0.25,
"discount": null,
"discountAmount": 0,
"total": 0.50
},
...
],
"canTopup": true
}
}Response (prepaid giga order)
{
"orderId": "80-123456",
"proxyTypeId": "residential-giga",
"proxyTypeName": "Residential Giga",
"paymentModel": "prepaid",
"unit": "giga",
"renewal": null,
"trafficTopup": {
"options": [
{
"trafficGb": 1,
"pricePerGb": 1.5,
"subtotal": 1.50,
"discount": null,
"discountAmount": 0,
"total": 1.50
},
...
],
"canTopup": true
}
}
renewalisnullfor prepaid types (no renewal β buy more traffic instead). Traffic top-up options use the same pricing as the initial purchase for that type. Frontend can use presetoptionsor call the custom preview endpoint below.
Preview Traffic Top-up Auth required
GET /orders/:id/traffic-preview
Preview traffic top-up price for a custom GB amount.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Query Parameters
| Name | Type | Description | Required |
|---|---|---|---|
trafficGb | number | GB amount to preview | Yes |
Response
{
"trafficGb": 25,
"pricePerGb": 0.05,
"subtotal": 1.25,
"discount": null,
"discountAmount": 0,
"total": 1.25
}Errors
{ "message": "order.invalid_traffic_gb" }
{ "message": "order.not_found" }Renew Order Auth required
POST /orders/:id/renew
Manually renew a postpaid order for the same billing period. Deducts from balance.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Request Body
No body required β sends a renewal at current pricing.
Response
Full order object.
Errors
{ "message": "order.not_postpaid" }
{ "message": "order.not_renewable" }
{ "message": "order.insufficient_balance" }
{ "message": "order.charge_locked" }Top-up Traffic Auth required
POST /orders/:id/topup-traffic
Add traffic GB to any order (prepaid or postpaid). Deducts from balance.
Pricing uses the same model as purchase:
- Prepaid giga:
pricePerUnitΓ GB, withtrafficDiscountsapplied. - Postpaid IP:
trafficPricePerGbΓ GB, withTRAFFIC_VOLUME_DISCOUNTSapplied.
Use GET /orders/:id/pricing or GET /orders/:id/traffic-preview to see exact prices before buying.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Request Body
| Name | Type | Description | Required |
|---|---|---|---|
trafficGb | number | GB to add (must be > 0) | Yes |
{ "trafficGb": 10 }Response
Full order object.
Errors
{ "message": "order.not_active" }
{ "message": "order.insufficient_balance" }
{ "message": "order.charge_locked" }Get Renewal Config Auth required
GET /orders/:id/renewal-config
Get auto-management settings for an order. Returns defaults if never configured.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Response
{
"autoRenew": true,
"autoTopupGb": true,
"autoTopupAmount": 10,
"autoTopupThresholdPercent": 80
}| Name | Type | Description | Required |
|---|---|---|---|
autoRenew | boolean | Postpaid only β auto-renew 24 h before expiry (default: true) | β |
autoTopupGb | boolean | Auto-buy traffic when usage hits threshold (default: true) | β |
autoTopupAmount | number | GB to purchase per auto top-up (default: 10) | β |
autoTopupThresholdPercent | number | Usage % that triggers auto top-up, 1β99 (default: 80) | β |
Update Renewal Config Auth required
PATCH /orders/:id/renewal-config
Update auto-management features. All fields optional β send only what you want to change. Creates the config row with defaults on first call.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:id | string | Order ID | Yes |
Request Body
{
"autoRenew": true,
"autoTopupGb": true,
"autoTopupAmount": 20,
"autoTopupThresholdPercent": 75
}Response
Same shape as GET /orders/:id/renewal-config.
Errors
{ "message": "order.not_postpaid" }
{ "message": "order.invalid_topup_amount" }
{ "message": "order.invalid_topup_threshold" }Renewal Config in Order Responses
Every order object (from GET /orders, GET /orders/:id, etc.) includes a renewalConfig field:
{
"id": "80-472109",
"status": "active",
"renewalConfig": {
"autoRenew": true,
"autoTopupGb": true,
"autoTopupAmount": 10,
"autoTopupThresholdPercent": 80
}
}Auto-Management Summary
| Feature | Trigger | Endpoint to configure |
|---|---|---|
| Auto-renew | ~24 h before expiry (cron) | PATCH /orders/:id/auto-renew |
| Auto traffic top-up | usage β₯ threshold (cron every 10 min) | PATCH /orders/:id/auto-topup |
| Peer-triggered top-up | peer reports exhaustion | internal β POST /internal/peer/traffic/exhausted |
Proxy Download (Public)
List Export Formats
GET /proxy/formats
List available export formats. Public β no auth required.
Response
{
"formats": [
{ "id": "txt", "name": "Plain text β host:port:user:pass", "extension": "txt" },
{ "id": "user-pass-at", "name": "user:pass@host:port", "extension": "txt" },
{ "id": "proto-url", "name": "proto://user:pass@host:port", "extension": "txt" },
{ "id": "curl", "name": "cURL commands", "extension": "sh" },
{ "id": "json", "name": "JSON array", "extension": "json" },
{ "id": "csv", "name": "CSV β host,port,user,pass,proto", "extension": "csv" }
]
}Download Proxy List
GET /proxy/dl/:hash
Download proxy list by public hash. Public β no authentication required.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
:hash | string | The order's publicHash | Yes |
Query Parameters
| Name | Type | Description | Required |
|---|---|---|---|
format | string | Export format id from /proxy/formats (default: txt) | No |
proto | string | Protocol: http or socks5 (default: http) | No |
Returns a file download with the appropriate content type.
Example txt response:
us1.fonproxy.com:10000:u_a1b2c3:xYz9q2Wp4rT8mN1k
us1.fonproxy.com:10002:u_a1b2c3:xYz9q2Wp4rT8mN1kExample curl response:
curl --proxy u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10000 https://httpbin.org/ip
curl --proxy u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10002 https://httpbin.org/ipExample curl with socks5 (?proto=socks5):
curl --socks5 u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10000 https://httpbin.org/ipThe hash is visible in the order response as
publicHash. Reset it withPOST /orders/:id/reset-hashif compromised.