Webhooks

ZephyrCart pushes events to endpoints you register. Endpoints must respond 2xx within 10 seconds to count as delivered.

Register an endpoint

curl -sS https://api.zephyrcart.io/v1/webhook_endpoints \
  -H "Authorization: Bearer $ZEPHYR_API_KEY" \
  -d '{
    "url": "https://hooks.example.com/zephyr",
    "events": ["order.created", "order.refunded"],
    "description": "ERP bridge — production"
  }'

The response includes a signing_secret starting whsec_…. Store it; you'll need it to verify signatures.

Verify signatures

Every delivery carries:

  • X-ZephyrCart-Delivery-Id — unique ULID per delivery
  • X-ZephyrCart-Delivery-Attempt1 on first send, increments on retry
  • X-ZephyrCart-Timestamp — Unix seconds, used in the signature
  • X-ZephyrCart-Signaturet=<ts>,v1=<hex-hmac-sha256>

The signed string is <timestamp>.<raw-request-body>. Compute HMAC-SHA256 with the signing secret and compare in constant time:

import hmac, hashlib, time

def verify(payload: bytes, header: str, secret: str, tolerance_seconds: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts, sig = int(parts["t"]), parts["v1"]
    if abs(time.time() - ts) > tolerance_seconds:
        return False
    expected = hmac.new(
        secret.encode(),
        f"{ts}.".encode() + payload,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, sig)

A request that fails verification must be rejected with HTTP 401. Do not log the body.

Retries

Deliveries retry on any non-2xx or timeout, with exponential backoff capped at 24 hours total — roughly 1m, 5m, 15m, 1h, 3h, 6h, 12h, then we give up and mark the delivery failed. Persistent failures put the endpoint in status=failing after 24 hours; the dashboard shows the last 100 delivery attempts.

Event catalogue

EventWhen fired
cart.createdNew cart
cart.updatedLine items, addresses, or coupons changed
cart.abandonedNo activity for 60 minutes
checkout.session.createdHosted checkout URL minted
checkout.session.completedPayment authorised
order.createdPlaced; first webhook after checkout.completed
order.updatedMutable-field change
order.refundedEach refund, full or partial
order.cancelledBefore fulfilment
customer.created
customer.mergedtarget is folded into source
product.created
product.archivedSoft-delete
inventory.lowConfigurable threshold per product