IPXO Unified Services API · v1.0.0

Do the most common tasks entirely through the API

Each workflow below is an ordered recipe — real endpoints, in the order an integration calls them. Every request shows its parameters and a sample successful response. Filter by what you do, paste your tenant UUID once, and every sample updates.

Used in every path below (placeholder is the docs example, not a live tenant). Parameter tables omit two things every call needs: the Authorization: Bearer header and the tenantUUID/user_id path segment. Responses labeled sample come from the API spec; anything labeled from backend source was read from the services' validation rules and response resources on 2026-07-22 (field names are real; example values are illustrative).

Everyone

Before you begin: authentication and the systems map

All requests go to https://apigw.ipxo.com with an OAuth2 bearer token. The gateway fronts three subsystems with different path shapes — knowing which is which saves most integration confusion.

  1. Get an access token

    POSThttps://hydra.ipxo.com/oauth2/token
    Parameters · 2
    ParamInDescription
    grant_typeform · requiredAlways client_credentials.
    scopeformSpace-separated: billing, ecommerce, credits, nethub-data. Request only what you need.
    curl -s -X POST https://hydra.ipxo.com/oauth2/token \
      -u "$CLIENT_ID:$CLIENT_SECRET" \
      -d "grant_type=client_credentials" \
      -d "scope=billing ecommerce credits nethub-data"
    200Typical response (OAuth2 protocol-standard shape)
    {
      "access_token": "eyJhbGciOiJSUzI1NiIs…",
      "token_type": "bearer",
      "expires_in": 3599,
      "scope": "billing ecommerce credits nethub-data"
    }
    # Then send it on every call:
    curl -s https://apigw.ipxo.com/billing/v1/TENANT_UUID \
      -H "Authorization: Bearer $ACCESS_TOKEN"
  2. Know the three path shapes

    Your API identity is a tenant (company). The Ecommerce and Credits systems call the same identifier user_id — it's typically the same UUID.

    SystemPath shapeWhat lives there
    Billing / IP Market/billing/v1/{tenantUUID}/… and /billing/v1/common/…Marketplace search, leases, monetization services, payouts, LOAs
    Ecommerce/ecommerce/public/{user_id}/…Cart, checkout, orders, invoices, payment methods, addresses
    Credits/credits/public/{user_id}/…Credit balance
    NetHub/nethub-data/{tenantUUID}/prefixes/…Discovered prefix inventory with WHOIS / geo / BGP / RPKI metadata
    Sanity check your token and tenant in one call: GET /billing/v1/{tenantUUID} returns the full tenant profile (sample under Tenant setup), including feature flags like is_lessee and uses_ecommerce_for_market.
    Verified from the portal source (2026-07-22): every backend is namespaced at the gateway — Billing at /billing/v1/…, Ecommerce at /ecommerce/public/…, Credits at /credits/public/…. Unprefixed /v1/… or /public/… paths (as in the original spec) appear nowhere in production client code; all paths in this guide include the prefixes. One caveat: the portal configs reference the staging host (apigw.staging.ipxo.org); production host apigw.ipxo.com is per the spec — confirm before shipping externally.
Lessee

Lease a subnet, end to end

From marketplace discovery to a paid, active lease: search → reserve → set up billing → checkout → confirm. Announcing the subnet (LOA) is the next workflow.

  1. Search the marketplace

    GET/billing/v1/{tenantUUID}/market/search?prefix_length=24

    The primary discovery endpoint. Keep the pricing.uuid — you need it to reserve.

    Parameters · 19 · from backend source
    ParamInDescription
    prefix_lengthquery · requiredCIDR prefix length, integer 8–24.
    registrars[]queryRIR filter: afrinic, apnic, arin, lacnic, ripencc.
    price_minqueryNumeric > 0.
    price_maxqueryNumeric > 0.
    price_negotiablequeryBoolean — only negotiable listings.
    promotionalqueryBoolean — featured listings.
    addressqueryIPv4 prefix match (mutually exclusive with octets).
    octets[]queryLeading octets as array of ints 0–255 (mutually exclusive with address).
    geo_country_codequery2-letter country code.
    geo_city_namequery2–100 chars; letters, spaces, hyphens, periods, apostrophes only.
    geo_providers[]querymaxmind, ipgeolocation, dbip, ipregistry, ipinfo, ip2location.
    geo_provider_match_typequeryor (default) or and — how geo filters combine across providers.
    capabilities_rpki[]queryautomated, manual, unsupported.
    capabilities_whois_inetnum[]querySame values.
    capabilities_whois_routes[]querySame values.
    capabilities_whois_rdns[]querySame values.
    reservation_idqueryCommitment reservation ID.
    limitquery1–1000, default 100.
    sort[]queryprice, -price, address, -address (dash = descending).

    Recovered from the backend request validation (SearchRequest::rules), 2026-07-22 — the original spec documented only prefix_length. Note there is no server-side pagination: the portal sends limit=1000 and pages client-side.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/search?prefix_length=24&geo_country_code=US&registrars%5B%5D=arin&sort%5B%5D=price&limit=100" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": [{
        "notation": "40.183.32.0/24",
        "address_string": "40.183.32.0",
        "prefix_length": 24,
        "registrar": "arin",
        "pricing": { "uuid": "9df98051-0e47-4be7-96c2-f73101b010a2", "price": 215.51, "negotiable": false },
        "geo_data": { "maxmind": { "country_code": "US" }, "ipinfo": { "city_name": "New York City" } },
        "capabilities": { "rpki": "manual", "whois": { "inetnum": "automated", "rdns": "unsupported" } }
      }]
    }
    Alternative: GET /billing/v1/{tenantUUID}/market/ipv4?cidr=24 returns price tiers per billing cycle, e.g. "price": { "1": 39, "3": 117, "6": 234, "12": 468, "24": 936 }. Its filter set (from backend source): required cidr 8–24, plus registry (RIPENCC|LACNIC|AFRINIC|ARIN|APNIC) or registrar_uuids[], price_min/price_max, wants_to_negotiate, address/octets[], geo_databases[] (maxmind, ip2location, dbip, ipinfo), geo_country_code, geo_city_name, limit (10–1000), sort[].
  2. Reserve the subnet

    POST/billing/v1/{tenantUUID}/market/ipv4/services/reserve

    Holds the subnet so nobody else can lease it while you complete checkout.

    Parameters · 2
    ParamInDescription
    pricing_uuidbodyUUID of the pricing entry from the search result.
    billing_cyclebodyCycle in months: 1, 3, 6, 12, or 24.
    curl -s -X POST "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/reserve" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "pricing_uuid": "9df98051-0e47-4be7-96c2-f73101b010a2", "billing_cycle": 1 }'
    200Response shape · from backend source
    { "uuid": "RESERVATION_UUID", "address": "40.183.32.0", "cidr": 24,
      "price": 215.51, "commitment_period": 1, "created_at": 1769731200 }

    Already reserved by someone else → 409 "Subnet is already reserved".

  3. Create a billing address (first purchase only)

    POST/ecommerce/public/{user_id}/addresses

    Stored once, reused on every later cart and order. List existing ones with GET on the same path.

    Parameters · 11
    ParamInDescription
    company_namebody · requiredLegal company name (max 150).
    line_onebody · requiredStreet address line 1 (2–100 chars).
    line_twobodyStreet address line 2.
    citybody · requiredCity (max 90).
    province_codebodyState/province code (e.g. TX, NY); must exist for the country.
    country_codebody · requiredISO 3166-1 alpha-2 code; must match the tenant's country.
    postcodebody · requiredPostal code (max 20).
    contact_emailbodyBilling contact email (optional despite spec examples).
    contact_phonebodyBilling contact phone.
    vat_numberbodyVAT number (validated; see vat_validation_status on the stored address).
    company_codebodyCompany registration code.
    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/addresses" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{
        "company_name": "Acme Corp", "line_one": "123 Main St",
        "city": "New York", "province_code": "NY",
        "country_code": "US", "postcode": "10001",
        "contact_email": "[email protected]"
      }'

    200Returns the created address including its uuid (from backend source). One billing address per customer — a second create fails; required flags above are from the backend validation.

  4. Register a payment method (first purchase only)

    GET/ecommerce/public/{user_id}/gateways

    List available gateways to get the gateway config UUID — for Stripe it includes the publishable key for client-side tokenisation.

    curl -s "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/gateways" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    {
      "data": [
        { "uuid": "9e072516-8236-4fa7-be0c-9de0b1c47c67", "gateway": "stripe",
          "config": { "public_key": "pk_test_51IQTJqEZwbJqxJ4z…" } },
        { "uuid": "9e072516-8236-4fa7-be0c-9de0b1c47c68", "gateway": "paypal", "config": null }
      ],
      "meta": { "current_page": 1, "total": 2, "per_page": 10 }
    }
    POST/ecommerce/public/{user_id}/gateways/{gatewayConfig_uuid}/{paymentMethodType}
    Parameters · 2
    ParamInDescription
    gatewayConfig_uuidpath · requiredGateway configuration UUID from the list above.
    paymentMethodTypepath · requiredType to add: card or paypal.
    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/gateways/GATEWAY_CONFIG_UUID/card" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    204Payment method registered. No response body.

  5. Build the cart

    GET/ecommerce/public/{user_id}/cart

    Get-or-create: returns the customer's active cart, creating one if none exists — this is where cart_uuid comes from. There is no explicit create-cart endpoint, by design.

    curl -s "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/cart" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Response shape · from backend source
    { "data": { "uuid": "CART_UUID", "total": { … }, "sub_total": { … }, "tax_total": { … },
                "credits_total": { … }, "discount_total": { … }, "remaining_total": { … },
                "tax_breakdown": [ … ], "discount_breakdown": [ … ], "expires_at": "…" } }
    POST/ecommerce/public/{user_id}/cart/{cart_uuid}/lines
    Parameters · 3
    ParamInDescription
    cart_uuidpath · requiredUUID from the GET …/cart call above.
    price_idbody · requiredUUID of the pricing entry to add.
    quantitybodyUnits to add; defaults to 1.
    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/cart/CART_UUID/lines" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "price_id": "PRICE_UUID", "quantity": 1 }'

    200Returns the created cart line — uuid, title, quantity, unit_price, sub_total, tax_total, total, billing period (from backend source).

    POST/ecommerce/public/{user_id}/cart/{cart_uuid}/addresses/{customerAddress_id}
    Parameters · 2
    ParamInDescription
    cart_uuidpath · requiredCart UUID.
    customerAddress_idpath · requiredNumeric ID of the address created in step 3.
    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/cart/CART_UUID/addresses/ADDRESS_ID" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Returns the associated address (same AddressResource shape as step 3) — from backend source.

    PATCH/ecommerce/public/{user_id}/cart/{cart_uuid}/payment-method/{paymentMethod_uuid}
    Parameters · 2
    ParamInDescription
    cart_uuidpath · requiredCart UUID.
    paymentMethod_uuidpath · requiredUUID of the registered payment method (step 4).
    curl -s -X PATCH "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/cart/CART_UUID/payment-method/PAYMENT_METHOD_UUID" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Returns the updated cart with recalculated totals (from backend source).

    Resolved 2026-07-22: the previously flagged "no create-cart endpoint" gap isn't one — GET …/cart above creates the cart implicitly (CartManager::getOrCreateCurrent). Worth a one-line confirmation from dev that this implicit contract is supported for external integrators. Optional: apply a promo code with POST …/cart/{cart_uuid}/discount-code/{code} (200 with updated totals, or 204).
  6. Checkout

    POST/ecommerce/public/{user_id}/cart/{cart_uuid}/checkout

    Charges the selected payment method, creates the order and invoice, and consumes the cart. Gateway processing fees appear as separate order lines.

    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/cart/CART_UUID/checkout" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Response shape · from backend source
    { "data": {
        "order": { "uuid": "ORDER_UUID", "status": "…", "placed_at": "…", "expires_at": "…",
                   "sub_total": { … }, "tax_total": { … }, "total": { … }, "remaining_total": { … } },
        "data": <payment-gateway response, null when fully covered by credits>
    } }

    The order.uuid here is what you use with the orders endpoints below.

  7. Confirm the lease is active

    GET/billing/v1/{tenantUUID}/market/ipv4/services

    Your new lease appears as a billing service with status: "active" and the serviceUUID you'll use for LOAs and termination.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": [{
        "billing_service": {
          "address": "68.164.7.0", "cidr": 24,
          "recurring_amount": 115, "status": "active",
          "next_due_date": 1775088000,
          "uuid": "a0f616fb-e93c-4371-b75c-c16ad8483a44",
          "pricing": { "uuid": "a0222aaf-5e33-4903-bccd-80f171c81ba4", "wants_to_negotiate": false }
        },
        "loa": [{ "uuid": "a0fff148-29a1-4d52-9c2a-43a860bd9e2c", "asn": 213060,
                  "as_name": "IPXO-Testing-ASN", "status": "Active" }],
        "market_service": { "expires_at": null, "registry": "arin",
                            "uuid": "a0222aaf-3da0-41a5-ac6f-15e256f3e2e0" }
      }]
    }
Lessee

Validate your ASN and get LOA documents

Before you can announce a leased subnet from your network, the ASN–subnet pair is validated and a Letter of Authorization is issued.

  1. Check the ASN is clean

    GET/billing/v1/common/asn/validate/{asn}
    Parameters · 1
    ParamInDescription
    asnpath · requiredAutonomous System Number to validate (integer).
    curl -s "https://apigw.ipxo.com/billing/v1/common/asn/validate/13335" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    {
      "asn": 13335, "valid": true,
      "as_name": "CLOUDFLARENET - Cloudflare, Inc.",
      "uce3": false, "ofac": false, "spamhaus": false,
      "country": "US", "status": "active"
    }
  2. Validate the subnet–ASN pair

    POST/billing/v1/{tenantUUID}/asn/validate/{asn}

    Checks route-origin authorization, RPKI validity, and registry records before an LOA is generated.

    Parameters · 2
    ParamInDescription
    asnpath · requiredASN to validate the subnets against.
    subnetsbodyArray of CIDR notations, e.g. ["68.164.7.0/24"].
    curl -s -X POST "https://apigw.ipxo.com/billing/v1/TENANT_UUID/asn/validate/213060" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "subnets": ["68.164.7.0/24"] }'
    200Response shape · from backend source
    { "asn": 213060, "valid": true, "as_name": "…", "country": "…",
      "ofac": false, "spamhaus": false, "uce3": false, "status": "active",
      "subnets": [ { "subnet": "68.164.7.0/24", "already_added": false } ] }

    Unknown ASN → 404.

  3. List and download LOAs

    GET/billing/v1/{tenantUUID}/market/ipv4/services/{serviceUUID}/loa
    Parameters · 1
    ParamInDescription
    serviceUUIDpath · requiredUUID of the leased Billing IPv4 service.
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/SERVICE_UUID/loa" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    {
      "data": [{
        "uuid": "a0fff148-29a1-4d52-9c2a-43a860bd9e2c",
        "asn": 213060,
        "as_name": "IPXO-Testing-ASN Internet Utilities Europe and Asia Limited",
        "status": "Active",
        "created_at": 1770211251
      }],
      "meta": { "current_page": 1, "from": 1, "last_page": 1, "per_page": 15, "to": 1, "total": 1 }
    }
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/SERVICE_UUID/loa/download" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -o loa-documents.zip

    200Binary application/zip archive of LOA PDFs.

  4. Revoke LOAs when re-homing (as needed)

    POST/billing/v1/{tenantUUID}/market/ipv4/services/loa/revoke

    Bulk-revokes LOAs across subnets. After revocation the previous ASN authorization is invalid — generate new LOAs before re-announcing.

    curl -s -X POST "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/loa/revoke" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "services_uuids": ["SERVICE_UUID"] }'

    200Empty (null) body. The body field is services_uuids[] — the service UUIDs whose LOAs to revoke (from backend source).

Lessee

Monitor, export, and terminate leases

Day-2 operations on active leases: inspect details and geodata, export for accounting, and end a lease the right way.

  1. Inspect a single lease

    GET/billing/v1/{tenantUUID}/market/ipv4/services/{serviceUUID}
    Parameters · 1
    ParamInDescription
    serviceUUIDpath · requiredUUID of the leased Billing IPv4 service.
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/SERVICE_UUID" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    {
      "billing_service": {
        "address": "68.164.7.0", "cidr": 24,
        "recurring_amount": 115, "status": "active",
        "start_date": 1769731200,
        "uuid": "a0f616fb-e93c-4371-b75c-c16ad8483a44",
        "ecommerce_subscription_uuid": "a0f61706-f2a4-45d3-8fcb-842da1541566"
      },
      "commitment": null,
      "pending_commitment": null,
      "market_service": { "expires_at": null, "registry": "arin",
                          "uuid": "a0222aaf-3da0-41a5-ac6f-15e256f3e2e0" },
      "termination_request": null
    }

    Geolocation often differs by provider — check all six transparently:

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/SERVICE_UUID/geodata" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": {
        "maxmind":     { "country_code": "US", "country_name": "United States", "state": "", "city_name": "" },
        "ipinfo":      { "country_code": "US", "country_name": "United States", "state": "Illinois",   "city_name": "Chicago" },
        "ip2location": { "country_code": "US", "country_name": "United States of America", "state": "Virginia", "city_name": "McLean" },
        "dbip":        { "country_code": "US", "country_name": "United States", "state": "California", "city_name": "San Jose" }
      }
    }
  2. Export your portfolio to CSV

    GET/billing/v1/{tenantUUID}/market/ipv4/csv
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/csv" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -o leases.csv

    200text/csv file: subnet address, CIDR, status, billing amounts, service dates.

  3. Terminate at end of billing cycle (default)

    POST/ecommerce/public/{user_id}/subscriptions/{subscription_uuid}/terminate

    End-of-cycle termination goes through the subscription, not the billing service. The lease stays active until the cycle ends, then terminates. Find the subscription first:

    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/subscriptions/search" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "filter": { "status": "active" }, "per_page": "100" }'
    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/subscriptions/SUBSCRIPTION_UUID/terminate" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "type": "end_of_period", "reason": "Pricing", "details": "Cancellation reason" }'

    200Creates a pending termination request; cancellable via /subscriptions/termination-requests/{uuid}/cancel until the period ends. (The former …/services/{serviceUUID}/terminate_request endpoint was removed from the gateway.)

  4. Terminate immediately (exceptional cases)

    GET/billing/v1/common/termination-reasons

    Immediate termination requires a valid reason UUID — fetch the list first.

    curl -s "https://apigw.ipxo.com/billing/v1/common/termination-reasons" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": [
        { "uuid": "86ee68ff-bead-462f-ad51-8c38225c6f61", "title": "Unauthorized 3rd party announcement" },
        { "uuid": "6a5abe4a-d8ac-4398-94c8-d94ca3c72bc3", "title": "The Subnet has been added to significant blocklist databases, such as Barracuda, Spamhaus, Spamcop, etc." },
        { "uuid": "84a4e2b8-8708-4fea-9c57-1b157a121389", "title": "ROA (Route Origin Authorization) is not generated within 48 hours after the assignment date." }
      ]
    }
    POST/billing/v1/{tenantUUID}/market/ipv4/services/{serviceUUID}/immediate-termination
    Parameters · 2
    ParamInDescription
    serviceUUIDpath · requiredUUID of the leased service.
    reason_uuidbodyUUID of a termination reason from the list above.
    curl -s -X POST "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/services/SERVICE_UUID/immediate-termination" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "reason_uuid": "86ee68ff-bead-462f-ad51-8c38225c6f61" }'

    200Returns { "status": "SUCCESS" }; the subnet enters quarantine (from backend source).

    Track quarantined subnets with GET /billing/v1/{tenantUUID}/market/ipv4/quarantine — a paginated list ({ data, meta }) of subnets in the post-termination cooling-off period.
Lessee

Orders, invoices, and credit balance

Everything finance asks for: what was charged, what's outstanding, and the PDFs.

  1. Check your credit balance

    GET/credits/public/{user_id}/balances
    Parameters · 1
    ParamInDescription
    filter[currency]queryISO 4217 currency code, e.g. USD, EUR.
    curl -s "https://apigw.ipxo.com/credits/public/TENANT_UUID/balances?filter%5Bcurrency%5D=USD" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    {
      "data": [{
        "available_balance": 9274.14,
        "total_balance": 9274.14,
        "currency": "USD",
        "updated_at": "2026-03-02T05:46:24+00:00"
      }]
    }
  2. Review orders and transactions

    GET/ecommerce/public/{user_id}/orders
    Parameters · 1
    ParamInDescription
    filter[status]querycompleted, expired, or pending.
    curl -s "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/orders?filter%5Bstatus%5D=completed" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": [{
        "uuid": "a0f616f9-6c9c-4d08-9176-3eae6a2a1d7e",
        "status": "completed",
        "placed_at": "2026-01-30T15:47:58+00:00",
        "sub_total": { "currency": "USD", "amount": "119.60", "amount_minor": 11960 },
        "total":     { "currency": "USD", "amount": "119.60", "amount_minor": 11960 },
        "remaining_total": { "currency": "USD", "amount": "0.00", "amount_minor": 0 }
      }]
    }

    Drill into a specific order — line items via …/orders/{uuid}/lines (gateway fees appear as separate lines), payments via:

    curl -s "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/orders/ORDER_UUID/transactions" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    {
      "data": [{
        "uuid": "a0f616f9-bc48-4dd9-be48-dd76c8a9f067",
        "type": "charge", "status": "completed",
        "gateway_driver": "stripe",
        "amount": { "currency": "USD", "amount": "119.60", "amount_minor": 11960 },
        "reference": "pi_3SvJvPEZwbJqxJ4z1HprQUmp",
        "created_at": "2026-01-30T15:47:58+00:00"
      }],
      "meta": { "current_page": 1, "total": 1, "per_page": 10 }
    }
  3. Recover a failed payment

    PATCH/ecommerce/public/{user_id}/orders/{publicOrder}/payment-methods/{paymentMethod_uuid}

    Swap the payment method on the pending order:

    curl -s -X PATCH "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/orders/ORDER_UUID/payment-methods/PAYMENT_METHOD_UUID" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    204Payment method updated on the order. No response body.

    Then re-run payment:

    curl -s -X POST "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/orders/ORDER_UUID/checkout" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Same shape as cart checkout — { "data": { "order": { uuid, status, totals… } } } plus the payment-gateway response (from backend source).

  4. Fetch invoices and PDFs — from both systems

    GET/ecommerce/public/{user_id}/invoices
    Parameters · 2
    ParamInDescription
    sortquerySort field and direction, e.g. -placed_at for newest first.
    filter[status]querypaid, unpaid, or refunded.
    curl -s "https://apigw.ipxo.com/ecommerce/public/TENANT_UUID/invoices?sort=-placed_at&filter%5Bstatus%5D=paid" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": [{
        "uuid": "a0f61705-0fad-4a80-b132-fe3554a6145a",
        "reference": "C4E00DCB-000001",
        "status": "paid",
        "placed_at": "2026-01-30T15:48:06+00:00",
        "sub_total": { "currency": "USD", "amount": "115.00", "amount_minor": 11500 },
        "total":     { "currency": "USD", "amount": "115.00", "amount_minor": 11500 },
        "invoicable_id": "a0f616f9-6c9c-4d08-9176-3eae6a2a1d7e",
        "invoicable_type": "order"
      }]
    }

    Line items via …/invoices/{uuid}/lines; the PDF via …/invoices/{uuid}/download. Billing-system invoices (marketplace charges) live separately:

    # billing-system invoices: single PDF or all-in-one export
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/invoices/INVOICE_UUID/pdf" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -o billing-invoice.pdf
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/invoices/export/pdf" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -o all-invoices.pdf

    200Binary application/pdf file(s).

Holder / Lessor

List a prefix for monetization

From "is my prefix eligible?" to a verified, live marketplace listing.

  1. Check the prefix is eligible

    POST/billing/v1/common/market/subnetValidity
    Parameters · 1
    ParamInDescription
    prefixesbody · requiredArray of CIDR notations to validate, e.g. ["68.164.7.0/24"].
    curl -s -X POST "https://apigw.ipxo.com/billing/v1/common/market/subnetValidity" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "prefixes": ["68.164.7.0/24"] }'
    200Sample response
    [{
      "address": "68.164.7.0", "cidr": 24,
      "status": true, "message": "",
      "minimum_split": 24, "maximum_split": 24,
      "registry": "arin"
    }]
  2. Understand commission before pricing

    GET/billing/v1/common/pricing/commissions

    Minimum commission, minimum per-IP price, and commission factor per CIDR size — price the listing above the floor.

    curl -s "https://apigw.ipxo.com/billing/v1/common/pricing/commissions" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "configuration": { "commission": {
        "24": { "min_commission": 9,    "min_ip_price": 0.035, "commission_factor": 0.15 },
        "23": { "min_commission": 18,   "min_ip_price": 0.035, "commission_factor": 0.15 },
        "16": { "min_commission": 2294, "min_ip_price": 0.035, "commission_factor": 0.15 }
      } }
    }
  3. Create the monetization service

    POST/billing/v1/{tenantUUID}/market/services

    Lists the prefix on the marketplace and triggers an ownership-verification email. Body recovered from the backend validation (AddToMarketRequest::rules), 2026-07-22:

    curl -s -X POST "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/services" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{
        "subnets": [{
          "address": "68.164.7.0", "cidr": 24, "registry": "arin",
          "minimum_split": 24, "maximum_split": 24,
          "pricings": [{ "cidr": 24, "value": 115, "wants_to_negotiate": false }]
        }],
        "tos_accepted": true,
        "subnet_actions_accepted": true
      }'

    201Created — returns the service collection; verification email sent.

    Body rules (from backend source): per subnet — address (IPv4), cidr 8–24, registry all required; optional hidden, split bounds 8–24, APNIC maintainer_id/maintainer_password. Per pricing — cidr + value (0 < value < 999999) required; selected_commitment_periods[] from 3, 6, 12, 24, 36, 60, 120 months, and it must be non-empty when wants_to_negotiate is true (empty when false). Top-level tos_accepted and subnet_actions_accepted must both be true. Pricing must cover every allowed split size.
  4. Verify ownership

    GET/billing/v1/common/market_auth_verify/{token}
    Parameters · 1
    ParamInDescription
    tokenpath · requiredVerification token from the ownership-confirmation email.
    curl -s "https://apigw.ipxo.com/billing/v1/common/market_auth_verify/VERIFICATION_TOKEN" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Service verified and activated. Body not documented in the spec.

    Didn't get the email? POST /billing/v1/{tenantUUID}/market/services/{ipmarketServiceUUID}/resend_verification (200 = resent; only valid while the service is pending verification).

  5. Confirm the listing is healthy

    GET/billing/v1/{tenantUUID}/market/services/{ipmarketServiceUUID}/status

    One report covering operational state, ownership auth, WHOIS, BGP, DNS, IP reputation (blocklists), and the LOA-A. Note: despite the spec's description, there is no RPKI field in this response.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/services/MARKET_SERVICE_UUID/status" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Response shape · from backend source
    { "abuse_email": "…", "registry": "arin", "status": "validity_active",
      "auth":  { "type": "…", "status": "…", "description": "…" },
      "whois": { "status": "…", "description": "…" },
      "bgp":   { "status": "…", "description": "…" },
      "dns":   { "status": "…", "description": "…" },
      "iprep": { "status": "…", "description": "…" },
      "loaa":  { "status": "…", "description": "…" } }

    Top-level status values: auth_pending, auth_failed_verify(±email variants), validity_pending, validity_active, validity_invalid, validity_terminated, object_terminated.

    Check what lessees will see with GET …/{ipmarketServiceUUID}/pricing (200; per-IP/per-subnet price, billing cycles, negotiability).

Holder / Lessor

Manage listings and child subnets

Reprice, control which sub-allocations are visible, audit changes, and retire listings.

  1. Update pricing or terms

    PATCH/billing/v1/{tenantUUID}/market/services/{ipmarketServiceUUID}

    Partial update — every field optional (from backend source): hidden, minimum_split/maximum_split (8–24), pricings[] (include a pricing's uuid to update it; each entry needs cidr + value, optional wants_to_negotiate + selected_commitment_periods[]), maintainer_id/maintainer_password. Only allowed while the service is in auth-pending or validity states. See lessee-facing pricing via GET …/pricing, and what active lessees currently pay via GET …/leased.

    200Returns the updated service resource (from backend source).

  2. Control child-subnet visibility

    GET/billing/v1/{tenantUUID}/market/ipv4/child/search
    Parameters · 3
    ParamInDescription
    addressquery · requiredBase IP of the parent subnet, e.g. 68.164.7.0.
    cidrquery · requiredCIDR mask of the parent subnet, e.g. 24.
    market_service_uuidquery · requiredUUID of the parent market service.
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/child/search?address=68.164.7.0&cidr=24&market_service_uuid=MARKET_SERVICE_UUID" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Available child sub-allocations. Body shape not documented in the spec.

    POST/billing/v1/{tenantUUID}/market/ipv4/child/toggle
    Parameters · 2
    ParamInDescription
    market_service_uuidbodyUUID of the parent monetization service.
    childrenbodyArray of child subnet identifiers to hide/unhide.
    curl -s -X POST "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/ipv4/child/toggle" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "market_service_uuid": "MARKET_SERVICE_UUID", "children": [ … ] }'

    200Visibility toggled. Body not documented in the spec.

    Audit what's hidden: GET …/market/services/{uuid}/ipv4/hidden/children?page=1&per_page=25page and per_page are both required query params.

  3. Audit every change with event logs

    GET/billing/v1/{tenantUUID}/market/services/event_logs

    Unified audit trail across all your listings — activations, terminations, pricing updates, lease assignments. Per-service logs at …/market/services/{uuid}/event_logs; full portfolio export at …/market/services/csv.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/services/event_logs" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Paginated { data, meta }; each entry (from backend source): label, type (pricing_updated | pricing_created | leased | released | created), data payload, created_at unix timestamp. No actor field.

  4. Extend or retire a listing

    DELETE/billing/v1/{tenantUUID}/market/services/{ipmarketServiceUUID}/expiration

    Cancels a scheduled expiration so the listing keeps running.

    200Expiration cancelled; service continues.

    To take the prefix off the marketplace entirely, DELETE …/market/services/{ipmarketServiceUUID} (200 = cancellation initiated) — active leases may run until their cycle ends depending on the termination policy.

Holder / Lessor

Get paid: payouts, invoices, deductions

Configure where money goes, watch earnings accrue, and pull the accounting paperwork.

  1. Set your payout method (once)

    PUT/billing/v1/{tenantUUID}/market/payoutmethod

    Bank transfer, PayPal, or platform credits. GET on the same path shows the current configuration — a 404 means none is configured yet.

    Parameters · from backend source
    ParamInDescription
    typebody · requiredbanktransfer, paypal, or credit.
    cyclebody · requiredPayout cycle in months: 0, 1, 3, 6, 12 (0 = on demand).
    details.beneficiary / address / bic / iban / bank_namebodyAll required when type=banktransfer.
    details.emailbodyRequired when type=paypal.
    minimal_amountbodyFloor is $1,000 for bank/PayPal, $0 for credits (enforced when cycle=0).
    curl -s -X PUT "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/payoutmethod" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "type": "paypal", "cycle": 1, "details": { "email": "[email protected]" } }'

    200Returns the payout-method resource (from backend source).

  2. Track earnings

    GET/billing/v1/{tenantUUID}/market/payouts/stats/v2

    Total sales, net payout after commission, and a per-period breakdown (prefer v2 over v1). CSV twin: GET …/stats/v2/export.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/payouts/stats/v2" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (new tenant — empty book)
    {
      "period": { "from": null, "to": null },
      "total_sales": 0,
      "total_net_payout": 0,
      "data": []
    }

    Individual payouts with status and self-billing invoice reference: GET …/market/payouts (paginated { data, meta }); single payout detail at …/market/payouts/{payoutUuid}.

  3. Pull self-billing invoices for accounting

    GET/billing/v1/{tenantUUID}/market/payouts_invoices

    IPXO issues these on your behalf each payout period.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/payouts_invoices" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Paginated envelope { "data": […], "meta": { current_page, last_page, per_page, total } }.

    Per invoiceEndpoint
    PDF documentGET …/payouts_invoices/{uuid}/pdf
    Per-lease items (gross / commission / net)GET …/payouts_invoices/{uuid}/items
    Aggregated totalsGET …/payouts_invoices/{uuid}/stats
    Bulk CSV of all invoicesGET …/payouts_invoices/export/csv

    Payment receipts (issued when IPXO sends the money): GET …/market/payment_confirmations, each with a /pdf.

  4. Reconcile commission deductions

    GET/billing/v1/{tenantUUID}/market/deductions

    Marketplace fees charged against earnings — amount, associated service, billing period.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/deductions" \
      -H "Authorization: Bearer $ACCESS_TOKEN"

    200Paginated envelope { "data": […], "meta": … }. Invoice PDFs at …/market/deductions/invoices/{invoiceUUID}/pdf.

Routing manager

Prefix inventory and metadata (NetHub)

Search your discovered prefix estate with enriched WHOIS / geo / BGP / RPKI data, and organize it with custom metadata. Path shape: /nethub-data/{tenantUUID}/prefixes/… — the gateway namespace is nethub-data (verified against the production gateway extract, 2026-08).

  1. Search prefixes with field selection

    POST/nethub-data/{tenantUUID}/prefixes/search
    Parameters · 5
    ParamInDescription
    fieldsbodyData objects to include: geodata, whois, bgp, rpki, routes; dot-notation sub-fields like whois.inetnum, routes.origin.
    geodatabodyFilter object { field, op, vals }; ops: eq, ne, gt, gte, lt, lte, and.
    whoisbodyFilter object { field, op, vals }, same operators.
    limitbodyMax prefixes to return — recommended for large estates.
    offsetbodyPrefixes to skip (pagination).
    curl -s -X POST "https://apigw.ipxo.com/nethub-data/TENANT_UUID/prefixes/search" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{
        "fields": ["geodata", "whois.inetnum", "whois.organisation", "whois.country"],
        "geodata": { "field": "countryCode", "op": "eq", "vals": ["US"] },
        "limit": 50, "offset": 0
      }'
    200Sample response (excerpt)
    {
      "data": [{
        "notation": "185.139.0.0/22", "maskSize": 22,
        "internalMetadata": {
          "master": true,
          "holder": { "tenantUUID": "c4a84238-3a89-43f0-8a83-8d5edc9c8c65",
                      "organisation": "ORG-CAF4-RIPE" }
        },
        "geodata": [
          { "provider": "dbip",    "countryCode": "DE", "cityName": "Frankfurt am Main" },
          { "provider": "maxmind", "countryCode": "DE", "cityName": "Frankfurt am Main" }
        ],
        "whois": { "inetnum": "185.139.0.0/22", "registrar": "ripencc",
                   "netname": "AE-CYBERASSETS-20160216", "country": "AE",
                   "organisation": "ORG-CAF4-RIPE" }
      }],
      "metadata": { "limit": 50, "offset": 0 }
    }
  2. Tag a single prefix

    PATCH/nethub-data/{tenantUUID}/prefixes/{notation}/metadata
    Parameters · 2
    ParamInDescription
    notationpath · requiredCIDR notation with the slash URL-encoded: 192.168.1.0/24192.168.1.0%2F24.
    (free-form)bodyAny key-value pairs to set on the prefix holder — labels, project IDs, regions.
    curl -s -X PATCH "https://apigw.ipxo.com/nethub-data/TENANT_UUID/prefixes/185.139.0.0%2F22/metadata" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '{ "projectID": "proj-12345", "label": "CDN Edge Range", "region": "EU-WEST" }'

    200Metadata set on the prefix holder. No response body documented.

  3. Bulk-tag by organisation, and manage list-valued tags

    PATCH/nethub-data/{tenantUUID}/prefixes/metadata
    Parameters · 2 (array items)
    ParamInDescription
    organisationbodyRIR organisation handle identifying the holder, e.g. ORG-CAF4-RIPE.
    metadatabodyKey-value pairs to set on that holder. Request body is an array of these entries.
    curl -s -X PATCH "https://apigw.ipxo.com/nethub-data/TENANT_UUID/prefixes/metadata" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '[{ "organisation": "ORG-CAF4-RIPE",
             "metadata": { "projectID": "proj-12345", "label": "Production Ranges" } }]'

    200Metadata set on the specified holders. No response body documented.

    For multi-valued fields (tags, contacts), use the …/metadata/array endpoints — POST appends (field, value), PATCH replaces by value (old_valuenew_value), DELETE removes:

    curl -s -X POST "https://apigw.ipxo.com/nethub-data/TENANT_UUID/prefixes/metadata/array" \
      -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
      -d '[{ "organisation": "ORG-CAF4-RIPE", "field": "tags", "value": "production" }]'

    200Elements appended to the metadata arrays. Single-prefix variants exist at …/prefixes/{notation}/metadata/array.

  4. Track bulk jobs and cloud integrations

    GET/billing/v1/{tenantUUID}/batches

    Bulk imports and mass updates run asynchronously as batches with status and progress.

    200Paginated envelope { "data": […], "meta": … } of batch operations.

    AWS BYOIP connected accounts (for provisioning leased subnets into EC2/ELB): GET /billing/v1/{tenantUUID}/integrations/aws/connected-accounts → 200 with { "data": […] }, empty if none configured.

LesseeHolder

Reputation and listing health

Watch blocklists before they become a support ticket, and read the market before you price.

  1. Review reputation scan history

    GET/billing/v1/{tenantUUID}/market/services/{ipmarketServiceUUID}/reputations

    Scan history against Spamhaus, Barracuda, and other blocklists / abuse databases — the deliverability health signal for monetized space.

    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/market/services/MARKET_SERVICE_UUID/reputations" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Response shape · from backend source
    { "data": [ { "id": "…", "ip": "…", "ip_int": …, "country_code": "…",
        "detection_rate": …, "detections": …, "detections_engine_list": [ … ],
        "engines_count": …, "is_listed": false, "is_proxy": false, "is_tor": false,
        "is_vpn": false, "isp": "…", "session_id": "…", "created": 1769731200 } ],
      "meta": { … } }
  2. Diagnose announcement issues

    GET/billing/v1/{tenantUUID}/market/services/{ipmarketServiceUUID}/status

    The same status report used at listing time doubles as the diagnostic when BGP, WHOIS, DNS, or reputation drift out of shape.

    200Shape shown under List a prefix step 5 — auth / whois / bgp / dns / iprep / loaa sub-objects; no RPKI field (from backend source).

  3. Read market supply and pricing

    GET/billing/v1/common/market/subnetUsage

    Free vs in-use counts and min/max/avg per-IP pricing by mask size — market intelligence for pricing decisions.

    curl -s "https://apigw.ipxo.com/billing/v1/common/market/subnetUsage" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    [
      { "mask": 24, "free": 15430, "in_use": 1250,
        "price_min": 0.15, "price_max": 2.50, "price_avg": 0.45,
        "period": "2025-07-01 00:00:00" }
    ]

    Supported registries with their UUIDs (used in search results): GET /billing/v1/common/market/registrars → 200, e.g. [{ "uuid": "6220a691…", "name": "arin" }, { "name": "ripencc" }, …].

Everyone

Tenant setup and reference data

Create and maintain the company account behind every other call.

  1. Create and inspect the tenant

    POST/billing/v1

    Creates the tenant and returns the UUID used in every subsequent call. Required fields (from backend source): title, email, address1, city, country (alpha-2), postcode, state, abuse_email, industry_uuid, and options.website (https URL). Optional: company_size, business_since, vat_number, options.social_network[], prorata settings.

    201Created — returns the tenant resource including its uuid (from backend source).

    GET/billing/v1/{tenantUUID}
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "uuid": "a0f5bdf6-499d-4679-9831-a9677abeed5c",
      "title": "Acme Corp",
      "email": "[email protected]",
      "address1": "123 Main St", "city": "New York", "postcode": "10001",
      "country": "US", "state": "New York",
      "status": "active",
      "abuse_email": "[email protected]",
      "industry": { "uuid": "cac34d86-00c5-11ec-87f1-cc8741ada327", "name": "Cloud Provider" },
      "credit": { "amount": 9274.14, "auto_payment": true },
      "flags": [
        { "slug": "uses_ecommerce_for_market" },
        { "slug": "is_lessee" }
      ],
      "email_verified": true,
      "abuse_email_verified": true
    }

    Update profile fields with PATCH /billing/v1/{tenantUUID} — only provided fields change (200 on success).

  2. Get the at-a-glance numbers

    GET/billing/v1/{tenantUUID}/details
    curl -s "https://apigw.ipxo.com/billing/v1/TENANT_UUID/details" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response
    { "monetizing_ips_count": 0, "renting_ips_count": 768, "code": null }
  3. Reference data for forms and validation

    GET/billing/v1/common/countries

    Countries with phone codes, tenant availability, and OFAC flags (Billing system, paginated).

    curl -s "https://apigw.ipxo.com/billing/v1/common/countries" \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    200Sample response (excerpt)
    {
      "data": [{
        "uuid": "75645c44-0554-4627-a032-e58508c5163b",
        "alpha_2_code": "AL", "alpha_3_code": "ALB",
        "name": "Albania", "phone_code": "+355",
        "tenant_availability": 1, "ofac_listed": 0
      }],
      "meta": { "current_page": 1, "last_page": 17, "per_page": 15, "total": 251 }
    }

    For address forms, Ecommerce has its own pair: GET /ecommerce/public/common/countries (name + alpha-2 code) and GET /ecommerce/public/common/countries/{country_code}/provinces (country_code path, required). Industry list for registration: GET /billing/v1/common/industries{ "data": [{ "uuid", "name": "Cloud Provider" }, …] }.