# Webhooks

Canonical URL: https://api-docs.test-printeers.com/guides/webhooks

Receive signed event notifications on your own endpoint the moment something happens on the Printeers platform, instead of polling the API.

> Looking for the single order-updated callback URL that predates this system? That mechanism keeps working unchanged and is documented in the [legacy webhook notification guide](https://api-docs.test-printeers.com/guides/legacy-webhooks.md). New integrations should use the webhooks described here.

A webhook endpoint is an HTTPS URL on your side that Printeers calls with an HTTP `POST` whenever an event you subscribed to occurs. The payload tells you *what happened* and *to which resource*; you then fetch the current state through the [API](https://api-docs.test-printeers.com/reference/v2). The payload format and the available event kinds are documented in the [Webhooks reference](https://api-docs.test-printeers.com/reference/webhooks).

The available event kinds follow an order through its lifecycle: `order.backordered`, `order.production_started`, `order.on_hold`, `order.hold_released`, `order.printed`, `order.completed` and `order.canceled`, plus `shipment.created`, fired when a shipment is created for one or more of your orders. Each kind is described in the [Webhooks reference](https://api-docs.test-printeers.com/reference/webhooks). More kinds will follow, but your endpoint only receives the kinds it is subscribed to, so new kinds are never delivered unannounced.

## Creating an endpoint

Webhook endpoints are managed in the Dashboard, under **Settings**. An endpoint belongs to your organization, not to a single store: one endpoint receives the events of **all your stores** for the event kinds it is subscribed to. If you route per store, read the references in the payload and dispatch on your side.

When you create an endpoint you choose which event kinds it is subscribed to, and the Dashboard shows you the endpoint's signing secret. Store the secret in your platform's secrets store; you need it to verify deliveries.

You can create multiple endpoints, each with its own URL, subscriptions, and secret, for example one per environment or one per consuming system.

## The event payload

Deliveries carry a deliberately thin JSON envelope: the event `reference`, the `kind`, when it happened, and a `subject` object holding the reference to the entity the event is about, keyed by the kind's subject prefix (`subject.shipment` for `shipment.*` kinds, `subject.order` for `order.*` kinds), plus a human-readable `message` for display only. It does not carry the subject's state, because that state may already have changed by the time you process the delivery.

On receipt, fetch the current state from the API using the reference in the payload. For a `shipment.created` event, call [`GET /shipments/{shipmentReference}`](https://api-docs.test-printeers.com/reference/v2#tag/shipments/GET/shipments/{shipmentReference}) with `subject.shipment.reference` to retrieve the tracking details and the shipped items. This way you always act on the latest state, no matter when or in what order deliveries arrive.

The exact envelope schema and each event kind are documented in the [Webhooks reference](https://api-docs.test-printeers.com/reference/webhooks). Unrecognized payload fields must be ignored: fields can be added at any time. The `message` field is for humans; its wording can change without notice, so never parse it.

## Verifying signatures

Every delivery is signed following the [Standard Webhooks](https://www.standardwebhooks.com/) specification. Three headers accompany each delivery:

| Header | Meaning |
| --- | --- |
| `webhook-id` | The event reference (`Event_...`). Identical across retries; use it to deduplicate. |
| `webhook-timestamp` | Unix timestamp (seconds) of this delivery attempt, refreshed per retry. |
| `webhook-signature` | `v1,` followed by the base64 HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{body}`, keyed with your endpoint secret. |

Verify the signature before trusting a delivery, and always verify against the **raw request body bytes**, not a re-serialized version of the parsed JSON. The easiest way is an off-the-shelf [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries), available for most languages. For example, in Node.js:

```javascript
import { Webhook } from "standardwebhooks";

const webhook = new Webhook(endpointSecret); // "whsec_..."

// Throws when the signature is invalid or the timestamp is too old.
const event = webhook.verify(rawBody, {
  "webhook-id": req.headers["webhook-id"],
  "webhook-timestamp": req.headers["webhook-timestamp"],
  "webhook-signature": req.headers["webhook-signature"],
});
```

The `webhook-signature` header can contain multiple space-separated signatures. The delivery is authentic if any one of them verifies; the standard libraries handle this for you.

## Rotating a secret

You can rotate an endpoint's secret in the Dashboard. After a rotation the previous secret stays valid for **24 hours**: during that grace period every delivery is signed with both the new and the previous secret, so you can deploy the new secret on your side without dropping deliveries. After the grace period only the new secret's signature is sent.

## Delivery and retries

- Respond with any **`2xx`** status code to acknowledge a delivery. Anything else, including timeouts and connection failures, counts as a failed attempt.
- Acknowledge fast: record the delivery, respond, and do your API fetch afterwards, out of band.
- Failed deliveries are retried with increasing backoff, up to **25 attempts spread over up to 8 days**. After that the delivery is permanently failed.
- Delivery is **at-least-once**: the same event can arrive more than once, and the same event is delivered to every endpoint subscribed to its kind. Deduplicate by the `webhook-id` header.
- Deliveries are **unordered**: a retried event can arrive after a newer event. Use the event's `created` timestamp to order them, or simply fetch current state on every delivery.

The Dashboard shows a delivery log per endpoint with every attempt, its response status, and the response your endpoint returned, so you can see exactly what was delivered and debug failures on your side. Log entries are kept for **90 days**; captured response bodies are kept for **10 days**.

## Failing endpoints are disabled automatically

If an endpoint keeps failing, Printeers disables it: an endpoint is automatically disabled once it has failed **10 or more consecutive attempts** sustained over **3 days**. A short outage never disables an endpoint.

A disabled endpoint receives nothing: events that occur while an endpoint is disabled are **not delivered to it and not queued** for later. Once your endpoint is fixed, re-enable it in the Dashboard; delivery resumes with new events from that point on, and the failure streak is reset so a repaired endpoint starts with a fresh disable window. Fetch anything you missed through the API.
