SETUP_GUIDES // CUSTOM_WEBHOOKS

Custom Webhook Order Intake

Send signed order data from a storefront, ERP, automation tool, script, or custom checkout into the same Gantry fulfillment queue used by native sales channels.

When To Use This

Custom storefronts

Send orders from a homegrown checkout without waiting for a native integration.

Automation tools

Let middleware push paid orders into Gantry after your own validation or transformation step.

Provider-light workflows

Keep payment or storefront credentials outside Gantry and send only the fulfillment data Gantry needs.

Setup Flow

  1. Open Workspace Settings, then Sales Channels.
  2. Create or expand a Custom Webhook channel.
  3. Copy the webhook URL and signing secret.
  4. Send a signed POST request with Content-Type: application/json.
  5. Confirm the order appears in Fulfillment after the worker processes the event.

Required Headers

  • X-Gantry-Timestamp: current Unix timestamp in seconds.
  • X-Gantry-Signature: sha256=<hex digest>.
  • X-Gantry-Event-Id: optional stable id for dedupe. If omitted, Gantry uses the order id.

The signature is HMAC-SHA256 of timestamp.raw_body using the channel signing secret. The raw body means the exact bytes sent in the request, before formatting or parsing changes.

Example Payload

Gantry normalizes accepted payloads into orders, order items, fulfillment records, and inventory auto-learning when that workspace feature is enabled.

{
  "order_id": "ORDER-1001",
  "customer": {
    "name": "Nova Lane",
    "email": "nova@example.com",
    "phone": "+15555550123"
  },
  "shipping_address": {
    "line1": "17 Pixel Court",
    "line2": "",
    "city": "Demo City",
    "state": "CA",
    "postal_code": "90210",
    "country": "US"
  },
  "items": [
    {
      "id": "line-1",
      "sku": "DEMO-BLUE",
      "name": "Demo MK.I Blue",
      "quantity": 1,
      "price": "29.99"
    }
  ],
  "amount_total": "29.99",
  "currency": "USD",
  "payment_status": "paid",
  "status": "open",
  "created_at": "2026-07-06T12:00:00Z"
}

Signing Example

import hashlib
import hmac
import json
import time
import requests

secret = "channel-signing-secret"
url = "https://fsr-labs.com/webhooks/custom/your-channel-id"

payload = {
    "order_id": "ORDER-1001",
    "customer": {"name": "Nova Lane", "email": "nova@example.com"},
    "shipping_address": {
        "line1": "17 Pixel Court",
        "city": "Demo City",
        "state": "CA",
        "postal_code": "90210",
        "country": "US",
    },
    "items": [{"sku": "DEMO-BLUE", "name": "Demo MK.I Blue", "quantity": 1}],
}

body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
timestamp = str(int(time.time()))
message = timestamp.encode("utf-8") + b"." + body
signature = hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest()

requests.post(
    url,
    data=body,
    headers={
        "Content-Type": "application/json",
        "X-Gantry-Timestamp": timestamp,
        "X-Gantry-Signature": f"sha256={signature}",
        "X-Gantry-Event-Id": "ORDER-1001-created",
    },
    timeout=10,
)

Operational Notes

  • Use a stable order id so retries update the same order instead of creating duplicates.
  • Include SKUs when available. If a SKU is missing, Gantry can still learn an item from the product name when auto-learning is enabled.
  • Keep secrets server-side. Do not place the signing secret in browser JavaScript.
  • If your source system retries events, reuse the same event id for the same event.
  • Custom webhooks are still subject to workspace plan limits and ingestion controls.