Orders API

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

Updated 2026-04-09 16:13:00
Orders API

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 }
}
NameTypeDescriptionRequired
proxyTypeIdstringProxy type ID from configYes
trafficGbnumberGB of trafficGiga types: Yes
countnumberNumber of IPsIP types: Yes
billingPeriodstringweek / month / yearNo
countriesobjectCountry β†’ count mapNo

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 countries is omitted or null, 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

NameTypeDescriptionRequired
ordersnumberNumber of ordersβ€”
ipsnumberTotal IPs purchased (count column sum)β€”
trafficGbnumberTotal traffic purchased in GBβ€”
spentUsdnumberTotal amount spent in USDβ€”

byStatus always includes all 6 statuses (zeros for statuses with no orders). byType.*.byStatus only includes statuses that have at least one order for that type.


List Orders Auth required

GET /orders

List user's orders (paginated).

Query Parameters

NameTypeDescriptionRequired
pagenumberPage number (default: 1)No
limitnumberItems per page (default: 20)No
typestringFilter by proxy type IDNo

Response

{
  "orders": [ ... ],
  "total": 42,
  "page": 1,
  "pages": 3
}

Get Order by ID Auth required

GET /orders/:id

Get a single order by ID.

Parameters

NameTypeDescriptionRequired
:idstringOrder IDYes

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.

NameTypeDescriptionRequired
state"ok" | "pending" | "partial"Current provisioning stateβ€”
assignedCountriesRecord<string,number> | nullCountries assigned, with count per countryβ€”
missingCountriesRecord<string,number> | nullCountries still missing IPs, with count neededβ€”
assignedCountnumberTotal IPs successfully assignedβ€”
missingCountnumberTotal IPs still missingβ€”

State meanings:

StateWhenWhat 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 state field is enough for the frontend to decide what to render.


Usage Fields

NameTypeDescriptionRequired
uploadBytesnumberTotal bytes uploadedβ€”
downloadBytesnumberTotal bytes downloadedβ€”
totalBytesnumberUpload + downloadβ€”
requestCountnumberTotal proxy requests processedβ€”
uploadGbnumberUpload in GB (3 decimal places)β€”
downloadGbnumberDownload in GB (3 decimal places)β€”
totalGbnumberTotal traffic in GBβ€”
maxBytesnumber | nullMax allowed bytes (giga orders only, null for IP orders)β€”
maxGbnumber | nullMax allowed GB (order.trafficGb for giga, null for IP)β€”
remainingBytesnumber | nullRemaining bytes before quota is exhaustedβ€”
remainingGbnumber | nullRemaining GBβ€”
usedPercentnumber | nullPercentage of quota used (0–100, 2 decimal places)β€”
lastReportedAtstring | nullISO timestamp of last peer usage reportβ€”

For giga orders (unit: 'giga'): maxGb equals the purchased trafficGb. Frontend can show a progress bar using usedPercent. For IP orders (unit: 'ip'): maxBytes, maxGb, remainingBytes, remainingGb, usedPercent are all null β€” 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 modelBehaviour
prepaidBalance deducted immediately. IPs auto-assigned by country. Order always activated β€” even if some countries had insufficient IPs (partial). provisioning.state = "partial" with missingCountries breakdown.
postpaidOrder 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:

StateStatusWhat to show
"ok"activeNormal order view
"pending"pendingFull-page "Setting up your order…" banner with missingCountries table. Order not running.
"partial"activeWarning banner "Running with partial IPs β€” some locations pending" + missingCountries table. IPs are working.

Admins can use POST /admin/orders/:id/ips/assign-by-country or POST /admin/orders/:id/ips/add-by-country to 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 index field 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

NameTypeDescriptionRequired
:idstringOrder IDYes

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 1 is missing β€” that IP was hidden by admin. The user sees 0, 2 with ports 10000, 10002.

Use credentials.username / credentials.password to authenticate with host:port.

If no peer is active, host and port will be null.

For pending orders (provisioning.state === "pending"), ips will be empty or partial and provisioning.missingCountries shows what's still being allocated. Show a "waiting for IPs" banner.

For partial orders (provisioning.state === "partial"), ips contains the assigned IPs and provisioning.missingCountries shows 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

NameTypeDescriptionRequired
:idstringOrder IDYes

Response

{ "publicHash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" }

Update Order Settings Auth required

PATCH /orders/:id/settings

Update user-editable order settings. All fields optional.

Parameters

NameTypeDescriptionRequired
:idstringOrder IDYes

Request Body

NameTypeDescriptionRequired
namestring | nullCustom label. Pass null to clear. Falls back to order ID in responses.No
proxyUsernamestringNew proxy username. Letters and digits only. Stored as u_<value>.No
proxyPasswordstringNew 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

NameTypeDescriptionRequired
:idstringOrder IDYes

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

NameTypeDescriptionRequired
:idstringOrder IDYes

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
  }
}

renewal is null for 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 preset options or 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

NameTypeDescriptionRequired
:idstringOrder IDYes

Query Parameters

NameTypeDescriptionRequired
trafficGbnumberGB amount to previewYes

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

NameTypeDescriptionRequired
:idstringOrder IDYes

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, with trafficDiscounts applied.
  • Postpaid IP: trafficPricePerGb Γ— GB, with TRAFFIC_VOLUME_DISCOUNTS applied.

Use GET /orders/:id/pricing or GET /orders/:id/traffic-preview to see exact prices before buying.

Parameters

NameTypeDescriptionRequired
:idstringOrder IDYes

Request Body

NameTypeDescriptionRequired
trafficGbnumberGB 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

NameTypeDescriptionRequired
:idstringOrder IDYes

Response

{
  "autoRenew": true,
  "autoTopupGb": true,
  "autoTopupAmount": 10,
  "autoTopupThresholdPercent": 80
}
NameTypeDescriptionRequired
autoRenewbooleanPostpaid only β€” auto-renew 24 h before expiry (default: true)β€”
autoTopupGbbooleanAuto-buy traffic when usage hits threshold (default: true)β€”
autoTopupAmountnumberGB to purchase per auto top-up (default: 10)β€”
autoTopupThresholdPercentnumberUsage % 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

NameTypeDescriptionRequired
:idstringOrder IDYes

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

FeatureTriggerEndpoint to configure
Auto-renew~24 h before expiry (cron)PATCH /orders/:id/auto-renew
Auto traffic top-upusage β‰₯ threshold (cron every 10 min)PATCH /orders/:id/auto-topup
Peer-triggered top-uppeer reports exhaustioninternal β€” 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

NameTypeDescriptionRequired
:hashstringThe order's publicHashYes

Query Parameters

NameTypeDescriptionRequired
formatstringExport format id from /proxy/formats (default: txt)No
protostringProtocol: 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:xYz9q2Wp4rT8mN1k

Example 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/ip

Example curl with socks5 (?proto=socks5):

curl --socks5 u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10000 https://httpbin.org/ip

The hash is visible in the order response as publicHash. Reset it with POST /orders/:id/reset-hash if compromised.

FonProxy Orders API β€” Proxy Orders, Renewal & Traffic Management