Agent skill
Printify Webhooks Skill
Receive and verify Printify webhooks. Use when setting up Printify webhook handlers, debugging X-Pfy-Signature HMAC-SHA256 verification, creating webhook subscriptions through the Printify API, or handling print-on-demand events like order:created, order:shipment:created, order:shipment:delivered, product:publish:started, or shop:disconnected.
Install this skill
npx skills add hookdeck/webhook-skills --skill printify-webhooks
When to Use This Skill
- How do I receive Printify webhooks (orders, shipments, product publishing)?
- How do I verify the Printify
X-Pfy-Signatureheader? - How do I create a Printify webhook and set a secret through the API?
- How do I handle
order:shipment:createdorproduct:publish:startedevents? - Why is my Printify webhook signature verification failing?
How Printify Secures Webhooks
Printify webhooks are created through the API (POST /v1/shops/{shop_), one per topic. The secret field is optional, but always set one: without it Printify sends no signature and anyone who finds your URL can post to it. Generate one with openssl rand -hex 20.
Verification (core)
Every request carries X-Pfy-Signature: sha256=<hex>. The value is the hex HMAC-SHA256 of the raw request body, keyed with your secret as a UTF-8 string (not base64-decoded). Strip the sha256= prefix before comparing. Printify has no webhook SDK, so verify manually against the raw body (don't JSON.parse first).
const crypto = require('crypto');
function verifyPrintifySignature(rawBody, header, secret) {
if (typeof header !== 'string' || !header.startsWith('sha256=')) return false;
const received = header.slice('sha256='.length).trim();
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
try {
return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
} catch {
return false; // different lengths
}
}
In Express, read the body with express.raw({ type: '*/*' }) so any content type verifies. There is no timestamp in the signature, so there is no replay window; dedupe on the event id.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
The payload is { id, type, created_. The event name is in type.
| Event | Triggered When | resource.data contains |
|---|---|---|
order:created | An order was created | Nothing |
order:updated | An order's status changed | shop_id, status |
order:line-item:canceled | A single line item was canceled | line_item_id, status, status_details |
order:sent-to-production | An order was sent to production | Nothing |
order:shipment:created | Some or all items were fulfilled | shipped_at, carrier, skus |
order:shipment:delivered | Some or all items were delivered | delivered_at, carrier, skus |
product:created | A product was created | shop_id |
product:updated | A product was updated | shop_id |
product:publish:started | Publishing to the storefront began | action, publish_details |
product:deleted | A product was deleted | shop_id |
shop:disconnected | A shop was disconnected | Nothing |
personalization-preview-task:processed | A preview task reached a terminal state | task_id, status, mockups |
For the full event reference, see Printify's webhook documentation.
Delivery Behavior
- Respond
200 OK. On a4xxor5xx, Printify retries up to 3 times, then blocks the webhook for 1 hour; no deliveries are sent during the block. - That makes returning
400for a bad signature costly if your secret is misconfigured. Test with the simulate endpoint first.
Environment Variables
PRINTIFY_WEBHOOK_SECRET=your_printify_webhook_secret # the "secret" passed when creating the webhook
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 printify --path /webhooks/printify
Reference Materials
- references/overview.md - Printify webhook concepts, events, and payloads
- references/setup.md - Creating webhooks and secrets through the API
- references/verification.md - Signature verification details and gotchas