Webhooks
Webhooks send an HTTPS POST to your endpoint when something happens in Termpact. Add endpoints in Settings → Webhooks (Owner or Admin). Webhooks are part of the Pro plan.
Events
Section titled “Events”| Event | Sent when |
|---|---|
invoice.created |
An invoice is issued, or an edited order replaces one (supersedes_id is set) |
payment.applied |
A payment is recorded and applied to invoices |
hold.changed |
An order or location hold is placed or released |
application.approved |
A trade account application is approved |
The request
Section titled “The request”POST /your/path HTTP/1.1Content-Type: application/jsonUser-Agent: Termpact-Webhooks/1Termpact-Event: invoice.createdTermpact-Delivery: 5c1e…Termpact-Signature: t=1767225600,v1=4f7c…{ "id": "0b6f…", "type": "invoice.created", "created_at": "2026-10-27T14:03:11.000Z", "shop_domain": "acme.myshopify.com", "api_version": "v1", "sequence": 1042, "data": { "invoice": { "id": "…", "number": "INV-1042", "total": { "amount": "1250.00", "currency": "USD" } } }}idis the event id. A replay or a retry sends the sameid, so use it to ignore duplicates.sequencegoes up by one for each event your store sends. Events can arrive out of order; sort bysequenceif order matters.datauses the same shapes as the API.
Reply with any 2xx within 10 seconds. Do the work after replying if it takes longer.
Check the signature
Section titled “Check the signature”The Termpact-Signature header holds a timestamp t and one or more v1 signatures. Each v1 is the hex HMAC-SHA256 of "<t>.<raw body>" with your endpoint’s signing secret. Accept the request when any v1 matches and t is within 5 minutes of your clock.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(header, rawBody, secret, nowSeconds = Math.floor(Date.now() / 1000)) { const parts = header.split(",").map((p) => p.trim().split("=")); const t = Number(parts.find(([k]) => k === "t")?.[1]); if (!Number.isInteger(t) || Math.abs(nowSeconds - t) > 300) return false; const want = Buffer.from(createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"), "hex"); return parts.some(([k, v]) => k === "v1" && /^[0-9a-f]{64}$/.test(v) && timingSafeEqual(Buffer.from(v, "hex"), want));}Use the raw request body exactly as received. Parsing and re-serializing the JSON changes the bytes and the check fails.
Rotating the secret
Section titled “Rotating the secret”Rotate secret creates a new secret and shows it once. For 24 hours each request carries two v1 signatures, one per secret, so you can update the receiver without missing anything.
Retries and switching off
Section titled “Retries and switching off”A delivery that doesn’t get a 2xx is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours. After that it shows as Gave up in the delivery log.
If an endpoint keeps failing for 3 days, Termpact switches it off and tells you in the app and by email. Fix the receiver, Turn on the endpoint, then use Replay in the delivery log for anything it missed.
Addresses we won’t send to
Section titled “Addresses we won’t send to”Endpoints must use HTTPS and a public address. Private, loopback, link-local and other reserved addresses are refused, and the host name is checked again on every send. Redirects are not followed.