FREDFRED docs

FRED webhooks: receive events and parcel tracking

Configure, secure and monitor your application's notifications.

Published by FRED

Create an integration

curl -X POST https://api.fredpod.com/v1/webhook_endpoints \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: creation-boutique-001" \
  -d '{"description":"My store","url":"https://your-app.example/webhooks/fred","events":["order.*","shipment.*"]}'

The webhooks:manage scope is required. The URL must use HTTPS, port 80 or 443 and a public destination, without credentials or a fragment. Private and local addresses and redirects are rejected; DNS resolution is checked and pinned for each delivery to limit SSRF attacks.

The creation response contains the whsec_… secret. Save it in your secrets manager: it does not appear in lists. Repeating the same creation with the same idempotency key retrieves its response. Secrets and these stored responses are encrypted in the database, and management HTTP responses carry Cache-Control: no-store.

GET /v1/webhook_endpoints/event-types provides the catalogue of events and families. Use specific types, a family such as order.*, or * alone. Empty lists and unknown types are rejected.

Test and production

An sk_test_… key operates in the sandbox; it cannot access production. From a seller session, management pages and routes accept ?mode=test to manage that same environment. A production key can also select its sandbox using this parameter.

Sandbox notifications are actually sent to the configured URL, with test data. They do not create any real charges.

Update your customer's order

Subscribe to order.* and shipment.*. Events use an envelope containing id, type and data.object.

EventStorefront action
order.in_productionDisplay “In production”
order.shippedDisplay “Shipped” and reload the parcels
order.deliveredDisplay “Delivered”
shipment.createdSave shipping and tracking information; the label exists, but the parcel may not have left yet
shipment.updatedUpdate the parcel's status and tracking link
shipment.deliveredRecord the reported delivery

Order events carry the ID in data.object.id. Shipment events carry data.object.order_id, data.object.tracking_number and data.object.tracking_url. Read GET /v1/storefront/orders/{order_id} again to get the current state and parcel list. Events do not necessarily arrive in order.

FRED sends parcel tracking to your application. Shipping labels and manufacturing files are managed by the workshop.

To check reception, call POST /v1/webhook_endpoints/{endpoint_id}/test. The 202 means the webhook.test event is queued; it creates neither an order nor a payment. Deliveries, failures, retries and secret rotations are managed in Developers → Webhooks in the dashboard.

Verify signatures

New endpoints use Standard Webhooks: remove the whsec_ prefix, base64-decode the secret, then compute HMAC-SHA256 over the bytes webhook-id.webhook-timestamp.raw_body. The body must not be parsed and serialised again before verification.

import base64
import hashlib
import hmac
import time


def verifier(secret: str, headers: dict, body: bytes) -> bool:
    try:
        msg_id = headers["webhook-id"]
        timestamp = headers["webhook-timestamp"]
        if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
            return False
        key = base64.b64decode(secret.removeprefix("whsec_"), validate=True)
        message = f"{msg_id}.{timestamp}.".encode() + body
        expected = base64.b64encode(hmac.new(key, message, hashlib.sha256).digest()).decode()
        return any(
            hmac.compare_digest(part[3:], expected)
            for part in headers["webhook-signature"].split()
            if part.startswith("v1,")
        )
    except (KeyError, ValueError, TypeError):
        return False

Keep a short clock tolerance (five minutes here) and deduplicate events by webhook-id. Respond with 2xx only after durably storing the event, then process it in your own queue. A successful delivery confirms the receiver's HTTP response, not the success of your business processing.

Legacy endpoints remain identified by signing_version: 1: they use the secret's characters after whsec_ as the HMAC key, without base64 decoding. They continue working until rotation. New and rotated endpoints use signing_version: 2.

During rotation, signatures from both secrets are sent for one hour. Your verifier must accept any valid signature among those separated by spaces. The new secret is displayed once and secret_rotates_until indicates when the transition ends. A second rotation is blocked during that hour.

Receive without duplicates

Delivery is at least once: deduplicate by webhook-id, durably store the event, then respond with 2xx. Process it afterwards in your work queue. On failure, FRED retries with increasing delays, up to eight attempts. Redirects are not followed.

Review attempts and retry terminal failures from the dashboard. Use the order GET to reconcile your tracking if your website has been unavailable.

Was this page helpful?

On this page