How to Connect a Digital-Goods Supplier API
Connecting a supplier API is not "write one curl and ship it." It is a service layer that has to survive somebody else's outages, must never hand the same code to two customers, and must not take your storefront down when the supplier deploys. Below is the working sequence, from first credential to go-live checklist.
Architecture — three layers, not two
The only shape that holds up in production is:
Supplier API → your middleware → storefront.
The middleware is your own service (or a set of background jobs plus internal endpoints) that holds the supplier key, keeps a local copy of the catalogue, places orders, polls their status, and hands the storefront nothing but normalised data.
Three things you must not do:
- Call the supplier API directly from a browser or mobile app. That means the key is on the client. A supplier key is a server-side secret, without exception.
- Hit the supplier API on every catalogue page render. You will hit rate limits and make your own availability hostage to someone else's.
- Put fulfilment logic in a storefront controller. Fulfilment must be an idempotent operation in a dedicated service, or a customer's double-click becomes a double order.
Step 1. Credentials and environments
The first thing you get is a key. Establish three facts about it before writing code:
- Which auth header. It might be
X-API-Key, it might beAuthorization: Bearer, it might be a request signature. FoxReload usesX-API-Key: YOUR_API_KEY— no Bearer tokens, no OAuth, no client_id/client_secret pair. - Whether the key can be retrieved again. With most suppliers, no. FoxReload shows the key exactly once, so it goes straight into a secret manager (Vault, AWS Secrets Manager, Doppler) rather than a
.envfile in your repo. - Whether IP allowlisting exists. If it does, turn it on. FoxReload keys can be restricted by IP or CIDR, up to 10 entries; a request from an unlisted IP returns
HTTP 403, which is a far better outcome than a leaked key that works from anywhere.
The environment question
Many suppliers give you a separate sandbox host. Many do not. FoxReload has no separate sandbox environment: you test with isMock: true in the order-creation body, which returns mock codes in the same response shape as a real order without debiting your balance.
This matters architecturally. Ask your supplier how testing actually works before you write your first integration test. If the test mode is a flag in the request body rather than a different host, your code has to thread that flag through the whole stack, and your database has to distinguish mock orders from real ones.
Step 2. Catalogue and price sync
The catalogue is a background job, not an on-demand request.
curl "https://public-api.foxreload.com/api/categories/?limit=20" \
-H "X-API-Key: YOUR_API_KEY"
Then pull products per category:
curl "https://public-api.foxreload.com/api/products/?category_id_or_slug=gift-cards&limit=20" \
-H "X-API-Key: YOUR_API_KEY"
Each product carries id, name and price. The id is what you later pass as itemId when placing an order, so persist it in your own table as the supplier's foreign key.
Practical sync rules:
- Store
supplier_product_idalongside your own internal SKU. Never bind your storefront directly to one supplier's identifiers, or adding a second supplier becomes a rewrite. - Refresh prices on a separate, more frequent job than the full catalogue crawl. Names and descriptions change rarely; prices change constantly.
- Never delete a product because it was absent from one response. Flag it
unavailableinstead. A partial response or a timeout must not wipe your catalogue. - Keep markup in your own system, not baked into the cost price. The supplier's price is an input; your retail price is the output of your own pricing engine.
Step 3. Balance checks
Wholesale digital-goods APIs usually run on a prepaid balance: you fund an account and orders debit it. That introduces a failure class that has nothing to do with your code — the order failed because you ran out of money.
What to build:
- Read the balance before placing an order when the amount is material.
- Run background balance monitoring with two thresholds — warning and critical — alerting to a channel somebody actually reads at 3 a.m.
- Handle the insufficient-funds error properly. On FoxReload it surfaces as
BalanceNotEnough, and top-ups go throughPOST /api/topups/crypto/. Ask your own supplier which funding methods they support and how long settlement takes, because that number directly determines how large a float you need to hold.
Think through the customer-facing side too. If your balance ran dry, the buyer should not get a 500. They should get a clear refusal — or, better, should never have been able to check out an item you cannot currently source.
Step 4. Placing the order
An order is a POST with an array of line items:
curl -X POST "https://public-api.foxreload.com/api/orders" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"itemId": "product_01krgfgww8eth9xvvysd6y7r4j",
"quantity": 1
}
],
"isMock": false
}'
The response is an order object with id, price, status, createdAt, paymentExpiresAt and an items[] array.
A necessary caveat about idempotency
Some APIs accept an Idempotency-Key header so that re-sending the same request cannot create a second order. Verify whether your specific supplier actually implements this — many do not. FoxReload does not support an Idempotency-Key header.
Where there is no support, you build duplicate protection yourself:
- Generate a unique operation key on your side and persist it before calling the supplier.
- On a network error or timeout, do not blindly re-place the order — first query the supplier's order list and check whether it was in fact created.
- Only once you have confirmed it does not exist do you retry.
A timeout on order creation is ambiguity, not failure. Treating it as failure and immediately retrying is the most expensive way to learn the difference.
Step 5. Status retrieval — polling as the baseline
Polling is the mechanism that always works. Webhooks are an optional extra.
Check whether your supplier offers webhooks and whether they sign them (HMAC or otherwise). If they do, treat webhooks as an accelerator — but keep polling as the safety net, because delivery is never guaranteed and your endpoint may be down at exactly the wrong moment. FoxReload offers no webhooks at all; order results are retrieved purely by polling.
curl "https://public-api.foxreload.com/api/orders/{order_id}" \
-H "X-API-Key: YOUR_API_KEY"
A workable polling schedule:
| Phase | Behaviour |
|---|---|
| First minute | Frequent polling, every few seconds |
| Afterwards | Widening interval (backoff) |
| Deadline | Your own cut-off, after which the order is flagged for attention |
| Terminal states | Polling stops — the outcome is recorded in your database |
FoxReload orders move active → paid → processing → completed, with terminal branches cancelled (carrying a cancelReason) and failed (with a per-item error in items[].error). On completed, codes sit in items[].externalData. The full state walkthrough is in the order flow article.
Step 6. Error handling
Split errors into three buckets and treat them differently:
- Never retry.
HTTP 400,401,403,404are your bug or your configuration. Retrying returns the same answer. Log and investigate. - Retry with backoff.
HTTP 429(rate limit) and5xx. Exponential backoff with jitter, a bounded attempt count, a ceiling on the interval. - Verify before retrying. Network timeouts on mutating calls. Find out what happened on the supplier's side first, act second.
Per-item errors inside an order are their own case. An order can complete partially: one line delivered, another not. Your data model has to represent partial fulfilment, or your refund logic will be wrong.
Step 7. Go-live checklist
- Key is in a secret manager, absent from git, and repo history has been checked.
- IP allowlist enabled if the supplier supports it.
- Mock (or sandbox) orders run against every product type you sell.
- Catalogue sync is scheduled and does not wipe on a partial response.
- Balance is monitored with two alert thresholds.
- Order placement is duplicate-protected on your side.
- Polling has a deadline and a stuck-order queue with an alert.
- Retries fire only on 429 and 5xx.
- Every supplier request and response is logged against your internal order ID — this is your evidence in a dispute.
- A manual runbook exists: what an operator does when an order hangs and the customer is already in your inbox.
Where to source inventory
An integration layer is worth building once, against a catalogue broad enough to cover most of your range. FoxReload is a wholesale digital-goods supplier with 900+ SKUs — game keys, gift cards, game top-ups, eSIM and software licences — all behind one REST API with automated delivery and multi-region SKUs. One key, one response shape, one order model across the whole range, instead of five integrations against five different auth schemes.
