B2B platform for digital goods

Order Status Polling and Notification Layer for Digital Goods

FoxReload does not send webhooks. To get order results, poll GET /api/orders/{order_id} until status reaches completed, cancelled, or failed. For high-volume stores that want event-driven delivery, this guide shows how to build a lightweight notification layer in front of polling.

Order Status Polling and Notification Layer for Digital Goods


Short Answer

FoxReload does not send webhooks. To find out when an order is complete, poll GET /api/orders/{order_id} until status is "completed", "cancelled", or "failed". When completed, items[].externalData contains the delivered codes. For high-volume stores that want event-driven behavior, build a background polling worker that converts status changes into internal events.


Important: FoxReload has no webhooks, no X-FoxReload-Signature header, and no order.* callback events. There is no sandbox — use isMock: true in order creation for test orders. There are no idempotency keys — if a network error leaves an order status uncertain, check GET /api/orders/{id} before creating a new one to avoid double-buying.


Key takeaway: Polling is the correct pattern for FoxReload. Most orders complete synchronously within seconds. For the cases that are slower, a background polling loop with exponential backoff gives you reliable async handling without webhooks.


Who This Guide Is For

  • Developers integrating the FoxReload API who need to handle async order fulfillment
  • Store operators building automated code delivery pipelines
  • Engineers who want event-driven delivery but are working with a polling API

How FoxReload Order Fulfillment Works

1. POST /api/orders/
   Body: {"items": [{"itemId": "product_01k...", "quantity": 1}]}
   → Response: {"id": "order_01k...", "status": "processing", ...}

2. Poll GET /api/orders/{order_id}
   → {"id": "order_01k...", "status": "processing", ...}   (not yet)
   → {"id": "order_01k...", "status": "completed",
      "items": [{"externalData": ["XXXXX-YYYYY-ZZZZZ"]}]}   (done)

When status == "completed", read items[i].externalData — this is an array of delivered codes or top-up confirmations. If an item has an error, check items[i].error.


Polling vs. Webhooks

Factor Polling (FoxReload) Webhooks (not available)
Supported by FoxReload Yes No
Latency Depends on poll interval (typically <3s) N/A
Implementation Simple loop Not applicable
High volume handling Background worker recommended N/A

Terminal Order Statuses

Status Meaning Action
completed Order fulfilled; codes in items[].externalData Deliver codes to customer
cancelled Order cancelled before fulfillment Refund customer; do not deliver
failed Fulfillment failed Alert ops; refund if payment was taken
processing / paid / active Still in progress Keep polling

Basic Polling Implementation

import time
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://public-api.foxreload.com"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

def poll_order(order_id: str, max_attempts: int = 30) -> dict:
    terminal = {"completed", "cancelled", "failed"}
    delay = 1.0
    for attempt in range(max_attempts):
        resp = requests.get(f"{BASE_URL}/api/orders/{order_id}", headers=HEADERS)
        resp.raise_for_status()
        order = resp.json()
        if order["status"] in terminal:
            return order
        time.sleep(delay)
        delay = min(delay * 1.5, 15)  # exponential backoff, cap at 15s
    raise TimeoutError(f"Order {order_id} did not reach terminal status")

def get_codes(order: dict) -> list[str]:
    codes = []
    for item in order.get("items", []):
        codes.extend(item.get("externalData", []))
    return codes

# Usage
order = poll_order("order_01k...")
if order["status"] == "completed":
    codes = get_codes(order)
    deliver_to_customer(codes)
elif order["status"] == "failed":
    alert_ops(order)

High-Volume: Background Polling Worker

For stores processing many orders per minute, run a dedicated background worker instead of blocking in-request polling:

Order creation API call
  → Store order_id in DB with status = 'pending'
  → Return response to customer ("Order placed, processing")

Background worker (runs every 2–5 seconds):
  → Query DB: SELECT * FROM orders WHERE status = 'pending'
  → For each: GET /api/orders/{order_id}
  → If status changed:
      - Update DB
      - If completed: deliver codes to customer (email / bot / order page)
      - If failed: alert ops

This decouples order creation from polling and scales cleanly. Your worker is the notification layer — FoxReload is not involved in pushing events.


Building Your Own Internal Notification Layer

If you want event-driven behavior inside your own system, emit internal events from the polling worker:

# After polling detects a status change:
if new_status == "completed":
    internal_queue.publish("order.completed", {
        "order_id": order_id,
        "codes": get_codes(order),
        "customer_id": customer_id,
    })

# Separate consumer:
@queue.consumer("order.completed")
def on_order_completed(event):
    send_code_to_customer(event["customer_id"], event["codes"])

This is entirely within your own infrastructure. FoxReload does not participate in this event flow.


Avoiding Double Orders (No Idempotency Keys)

FoxReload has no idempotency keys. If a network error makes it unclear whether an order was created, always check before creating a new one:

def safe_create_order(item_id: str, qty: int, your_ref: str) -> dict:
    # 1. Check if an order with your internal reference already exists
    existing = find_order_by_reference(your_ref)  # your DB lookup
    if existing:
        return poll_order(existing["foxreload_order_id"])

    # 2. Create new order
    resp = requests.post(
        f"{BASE_URL}/api/orders/",
        headers=HEADERS,
        json={"items": [{"itemId": item_id, "quantity": qty}]}
    )
    resp.raise_for_status()
    order = resp.json()
    save_order_reference(your_ref, order["id"])  # persist before polling
    return poll_order(order["id"])

Store your internal reference and the FoxReload order ID together before polling.


Handling Stuck Orders

If an order remains in processing beyond your SLA:

  1. Continue polling with backoff — some fulfillments are legitimately slow
  2. After your SLA threshold (e.g., 10 minutes), alert your ops team with the order ID
  3. Do not cancel and re-create automatically — check with FoxReload support first to avoid double-buying

Checklist

  • POST /api/orders/ with X-API-Key auth and application/json content-type
  • Store order ID immediately after creation
  • Poll GET /api/orders/{order_id} with exponential backoff
  • Handle all terminal statuses: completed, cancelled, failed
  • Extract codes from items[].externalData on completion
  • Check items[].error for per-item failures on completed orders
  • Before re-creating on network error, check if order already exists
  • For high volume: background polling worker with internal event emission
  • Alert on orders stuck in processing beyond SLA
  • Test with isMock: true orders before going live

Frequently asked questions

Does FoxReload support webhooks?
No. FoxReload does not send webhook callbacks. To get order results, poll GET /api/orders/{order_id} until status is 'completed', 'cancelled', or 'failed'. Most orders complete within seconds; polling every 1–3 seconds is the recommended approach.
Where are the delivered codes in the API response?
When status is 'completed', the items array contains externalData for each item. That field holds the array of delivered codes or top-up confirmation.
What if an order stays in 'processing' for a long time?
Continue polling with exponential backoff. If the order stays stuck beyond your SLA (e.g., 10 minutes), alert your ops team. Do not create a duplicate order without first checking the status — re-creating without checking can result in double-buying.
Do I need webhooks or can I poll the order status?
For FoxReload, polling is the only supported method. At 100 orders/day polling is straightforward. At higher volume, run a dedicated background worker that polls all pending orders on a schedule and emits internal events when status changes.
Can I build my own webhook-style notifications on top of polling?
Yes. Run a background poller that calls GET /api/orders/{id} for all pending orders. When status changes to 'completed', push a message to your internal queue (Redis, SQS, etc.) or fire an internal HTTP callback. This is fully within your own infrastructure — FoxReload is not involved.
Get FoxReload API access

Related articles