B2B platform for digital goods

Webhooks for Digital Goods Orders

Webhooks are HTTP push notifications sent from a supplier's system to your server when an order event occurs. For digital goods, the critical events are: order created, order fulfilled, order failed.

Webhooks for Digital Goods Orders


Short Answer

Webhooks are HTTP push notifications sent from a supplier's system to your server when an order event occurs. For digital goods, the critical events are: order created, order fulfilled, order failed. Instead of polling the order status endpoint repeatedly to check if a code is ready, webhooks notify your server the moment fulfillment completes. This makes delivery faster, reduces API calls and eliminates polling-related delays.


Definition: A webhook for digital goods orders is an HTTP POST request sent by the supplier to your server endpoint when an order changes status β€” for example, when a code is ready for delivery. Your server receives the event and processes delivery without needing to poll.


Key takeaway: Polling the order status endpoint works at small scale. At high volume, polling hits rate limits and introduces latency. Webhooks are the production-grade solution: one event per order change, delivered as it happens.


Who This Guide Is For

  • Developers integrating a digital goods API who need async order handling
  • Store operators whose order fulfillment sometimes has a delay (pending orders)
  • Anyone building a high-volume digital goods pipeline

Polling vs. Webhooks

Factor Polling Webhooks
How it works Your server asks "is the order done?" every N seconds Supplier notifies your server when status changes
Latency Depends on poll interval Near-instant
API call volume High (N calls per pending order) One call per event
Rate limit risk High at scale None
Implementation Simpler Slightly more complex (endpoint + validation)
Best for Development/testing Production

Webhook Event Types

For a digital goods supplier API, expect these event types:

Event When It Fires Action on Your Server
order.created Order received by supplier Log; start SLA timer
order.fulfilled Code ready; order complete Deliver code to customer
order.failed Fulfillment failed Alert ops; trigger refund flow if payment was taken
order.refunded Order reversed by supplier Update status; notify customer
balance.low Reseller balance below threshold Trigger balance top-up process

The most critical event is order.fulfilled β€” this is the trigger for code delivery.


Webhook Payload Format (Typical)

{
  "event": "order.fulfilled",
  "timestamp": "2026-05-01T14:23:11Z",
  "data": {
    "order_id": "SUP-99887",
    "reference": "ORD-12345",
    "status": "fulfilled",
    "items": [
      {
        "sku": "steam-20-usd",
        "code": "XXXXX-YYYYY-ZZZZZ",
        "pin": null
      }
    ]
  },
  "signature": "sha256=abc123..."
}

Always check the reference field β€” this is your internal order ID, which lets you match the supplier event to your database without storing the supplier's order IDs as primary keys.


Signature Validation

Suppliers sign webhook payloads so you can verify they came from a legitimate source. Without validation, anyone can POST to your endpoint.

HMAC-SHA256 validation (most common):

import hmac
import hashlib

def validate_webhook(payload_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    received = signature_header.replace('sha256=', '')
    return hmac.compare_digest(expected, received)

Rules:

  • Validate before processing any payload
  • Use constant-time comparison (hmac.compare_digest) to prevent timing attacks
  • Return HTTP 400 if signature validation fails (do not return 200)
  • Return HTTP 200 immediately if signature is valid β€” before processing the event

Async Processing

Process webhook events asynchronously. Your webhook endpoint must return HTTP 200 within the supplier's timeout window (typically 3–10 seconds). If your event processing takes longer β€” database writes, email sends, API calls β€” the supplier may mark the delivery as failed and retry.

Correct architecture:

Webhook endpoint receives POST
  β†’ Validate signature
  β†’ Return HTTP 200 immediately
  β†’ Push event to queue (Redis, SQS, DB queue table)

Background worker
  β†’ Pulls event from queue
  β†’ Processes: deliver code to customer, update order status

Wrong: Processing delivery inside the webhook handler before returning 200.


Retry Behavior

Suppliers retry webhook delivery if they don't receive HTTP 200 within the timeout. Typical retry schedules:

  • 1st retry: 30 seconds after failure
  • 2nd retry: 5 minutes
  • 3rd retry: 30 minutes
  • Beyond: varies (some suppliers retry for 24 hours)

Your idempotency handling must prevent processing the same order twice if a retry fires after you already processed the first attempt. Use the order reference as an idempotency key:

if order_already_delivered(reference):
    return HTTP 200  # Acknowledge, do nothing

Fallback: Polling for Missing Events

Webhooks can fail. Your endpoint can go down, or a network issue can prevent delivery. Implement a polling fallback for orders that remain in pending status for more than N minutes:

Scheduled job (every 5 minutes):
  β†’ Find all orders with status = 'pending' and age > 5 minutes
  β†’ For each: call GET /orders/:id
  β†’ If status = fulfilled: process delivery
  β†’ If status = failed: alert ops

This ensures no orders are stuck indefinitely due to a missed webhook.


Implementation Checklist

  • Register webhook endpoint URL with supplier (typically in API settings)
  • Implement POST endpoint that accepts webhook payloads
  • Read raw body before JSON parsing (for signature validation on raw bytes)
  • Implement signature validation using supplier's algorithm and your shared secret
  • Return HTTP 200 immediately after validation passes
  • Push event to async queue
  • Build background worker that processes events from queue
  • Implement idempotency check using order reference
  • Implement fallback polling for pending orders
  • Test with supplier's test event payload
  • Monitor for webhook delivery failures in supplier dashboard

Frequently asked questions

Do I need webhooks for a small-volume store?
At under 100 orders per day, polling works and is simpler to implement. Webhooks become necessary when polling creates rate limit pressure or latency issues at higher volume.
What happens if my webhook endpoint is down when an event fires?
The supplier retries. If your endpoint comes back up before the retry window expires, the event will be delivered and processed. This is why the fallback polling job is important β€” it catches any events that exhausted retries.
Can I use the same webhook endpoint for multiple event types?
Yes. Route based on the event field in the payload. Use a switch/case or if-else per event type.
How do I test my webhook locally during development?
Use a tunneling tool (ngrok, cloudflared) to expose your local server to the internet, then register the tunnel URL as your webhook endpoint with the supplier.
What should I do if I receive a webhook for an order I don't have in my database?
Log the unknown reference, return HTTP 200 (so the supplier doesn't retry), and investigate. This may indicate an order creation step that failed silently on your side.
Get FoxReload API access

Related articles