Skip to main content
This page shows the recommended webhook consumer pattern for Points.

Golden rules

  1. verify X-Webhook-Secret
  2. acknowledge quickly with 2xx
  3. enqueue work instead of processing inline
  4. deduplicate on (order.id, event)

Expected request

Points sends:
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
X-Webhook-Secret: abc123def456...
X-Webhook-Event: approved
With a JSON body like:
{
  "event": "approved",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "order_status": "approved"
  },
  "timestamp": "2026-04-18T10:05:12Z"
}

Verification example

Node.js

import crypto from "node:crypto";

function verifySecret(received, expected) {
  const a = Buffer.from(received || "", "utf8");
  const b = Buffer.from(expected || "", "utf8");
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

PHP

if (!hash_equals($expectedSecret, (string) request()->header('X-Webhook-Secret'))) {
    abort(401);
}

Python

import hmac

if not hmac.compare_digest(received_secret or "", expected_secret or ""):
    raise PermissionError("Invalid webhook secret")

Fast-ack pattern

Do this:
app.post("/webhooks/points", async (req, res) => {
  if (!verifySecret(req.header("X-Webhook-Secret"), process.env.POINTS_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }

  await queue.publish("points.webhook", req.body);
  return res.status(200).end();
});
Do not:
  • call slow third-party services before responding
  • send email synchronously
  • update ERP/WMS inline if it can block the response

Idempotency strategy

Deduplicate using:
  • order.id
  • event
Recommended database uniqueness rule:
create unique index points_webhook_event_dedupe
  on webhook_events (points_order_uuid, event);
If a duplicate arrives, return 200 and do nothing.

Status codes

Your responseMeaning
200 / 204Accepted
401 / 403Secret invalid
5xxTemporary processing failure; may trigger retry

Retry expectations

Current backend behavior:
  • each outbound request uses a 10 second timeout
  • the job retries up to 3 times on failure
Your side should therefore assume duplicates are normal.

Operational tips

A sudden wave of secret mismatches often means the secret was rotated on one side only, or the endpoint was copied incorrectly.
Store the raw JSON and headers for a limited retention period in staging/production so support can reconcile difficult cases.
Never reuse production webhook secrets in staging or local development.
See Security for broader hardening guidance, and Webhook events for payload schemas.