Gareth Wilson Gareth Wilson

Guide to Picqer Webhooks: Features and Best Practices

Published


Picqer webhooks notify your systems when warehouse and order-management activity happens: an order is created, a picklist is closed, a shipment is created. For integrations that sync Picqer with a shop, an ERP, or a shipping tool, webhooks are how you react in real time instead of polling the API.

This guide covers how Picqer webhooks work, the events you can subscribe to, how to verify the X-Picqer-Signature (and what to do when no secret is set), the delivery and retry behavior, and the best practices for running them in production.

What are Picqer webhooks?

Picqer webhooks are HTTP POSTs delivered to an endpoint you register, each carrying a JSON payload about a warehouse or order event. You create hooks through the Picqer API, subscribing each one to a specific event and, optionally, giving it a secret used to sign deliveries.

Picqer signs deliveries with a base64 HMAC-SHA256 in the X-Picqer-Signature header, but only when you set a secret on the hook.

Picqer webhook features

FeatureDetails
ConfigurationAPI only: POST /api/v1/hooks
API authenticationHTTP Basic (API key as username)
Signature headerX-Picqer-Signature
Signature schemebase64 HMAC-SHA256 of the whole raw body, keyed with the per-hook secret
SecretOptional at creation; without it, deliveries are unsigned
AcknowledgementRespond 200/201/202 within 10 seconds
Retries15 retries over ~17 hours
Auto-deactivationAfter 5 complete failures within 24 hours
Rate limit~500 req/min (dynamic); 429 + error code 28 on excess
SDKOfficial PHP client (picqer/api-client); no npm/pip

Common events

Picqer events use a dotted, resource-scoped naming convention. Order and picklist events are the ones most integrations start with:

EventFires when
orders.createdA new order is created
orders.status_changedAn order's status changes
picklists.closedA picklist is closed
picklists.shipments.createdA shipment is created for a picklist
products.free_stock_changedA product's free stock level changes
receipts.completedA receipt is completed

Note the nesting: shipment creation is picklists.shipments.created, not shipments.created. Subscribe only to the events you process, and consult Picqer's webhook reference for the full list.

Setting up Picqer webhooks

Hooks are created via the API (Basic auth, with your API key as the username). Provide a name, the event, the destination address, and optionally a secret:

curl -X POST "https://your-subdomain.picqer.com/api/v1/hooks" \
  -u "YOUR_API_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order created -> our app",
    "event": "orders.created",
    "address": "https://your-app.example.com/webhook",
    "secret": "a-secret-you-choose"
  }'

Set the secret. Without it there's no signature to verify. DELETE deactivates a hook rather than removing it, and POST /hooks/{id}/reactivate re-enables one.

Securing Picqer webhooks

When a hook has a secret, each delivery carries an X-Picqer-Signature header: a base64-encoded HMAC-SHA256 of the entire raw request body, keyed with that secret. Recompute it over the exact bytes and compare in constant time.

const crypto = require("crypto");

const SECRET = process.env.PICQER_WEBHOOK_SECRET;

function verify(rawBody, signature) {
  const expected = crypto.createHmac("sha256", SECRET).update(rawBody).digest("base64");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-picqer-signature"];
  if (!verify(req.body, signature)) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge within 10 seconds
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

The same check in Python:

import base64
import hashlib
import hmac
import os

SECRET = os.environ["PICQER_WEBHOOK_SECRET"].encode()


def verify(raw_body: bytes, signature: str) -> bool:
    digest = hmac.new(SECRET, raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode()
    return hmac.compare_digest(expected, signature or "")

The official SDK is PHP-only (picqer/api-client on Packagist); there's no official npm or pip package, so outside PHP you implement the base64 HMAC yourself.

Picqer webhook limitations and pain points

No secret means no verification

The Problem: The signing secret is optional at hook creation. Create a hook without one and Picqer sends no X-Picqer-Signature, leaving your endpoint unable to prove a delivery came from Picqer, so it acts on any POST that reaches the URL.

Why It Happens: Picqer makes signing opt-in per hook rather than mandatory.

Workarounds:

  • Always set a secret when creating a hook, and reject deliveries with no valid signature.
  • Audit existing hooks for missing secrets and recreate them with one.

How Hookdeck Can Help: Hookdeck gives every source a verified, dedicated URL and can enforce its own authentication in front of your endpoint, so an unsigned Picqer hook isn't your only line of defense.

Base64, not hex

The Problem: X-Picqer-Signature is base64-encoded, not hex. A verifier that hex-encodes the HMAC (the more common default) will fail every delivery.

Why It Happens: Picqer chose base64 for the signature encoding.

Workarounds:

  • Base64-encode the HMAC digest and compare against the header in constant time.
  • Verify against the whole raw body, exactly as received.

How Hookdeck Can Help: Hookdeck verifies the X-Picqer-Signature at the edge, so your app never has to get the encoding detail right.

Aggressive auto-deactivation

The Problem: Picqer retries a failed delivery 15 times over about 17 hours, and a hook is automatically deactivated after 5 complete failures within 24 hours. A flaky endpoint can silently switch a hook off, after which you receive nothing until you reactivate it.

Why It Happens: Deactivation stops Picqer from retrying hooks that look broken.

Workarounds:

  • Acknowledge with a 2xx fast so transient downstream issues don't count as complete failures.
  • Monitor hook status and use POST /hooks/{id}/reactivate to recover.
  • Log deliveries on receipt so you can reconcile anything missed while deactivated.

How Hookdeck Can Help: Hookdeck always accepts deliveries from Picqer and absorbs downstream failures itself, keeping the hook active while it retries to your service on a schedule you control.

Duplicates and rate limits on follow-up calls

The Problem: Retries mean the same event can arrive more than once, and because payloads often need a follow-up API read, high volume can hit Picqer's ~500 req/min limit (429 with error code 28).

Why It Happens: At-least-once delivery plus API-backed enrichment multiplies your request volume.

Workarounds:

  • Dedupe on a payload identifier and make side effects idempotent.
  • Back off on 429s using the rate-limit response, and batch or cache follow-up reads.

How Hookdeck Can Help: Hookdeck deduplicates deliveries and can rate-limit forwarding to match your API budget, so retries don't double-process and bursts don't trip Picqer's limits. See our guide to webhook idempotency.

Best practices

Always set a secret and verify with base64

Create hooks with a secret, and verify the base64 HMAC-SHA256 over the whole raw body in constant time. Reject unsigned deliveries.

Acknowledge within ten seconds, process asynchronously

Return a 2xx quickly and defer work to a queue so transient slowness doesn't count toward the 5-failures deactivation threshold. See why to process webhooks asynchronously.

Use the exact (nested) event names

Subscribe with the precise names, including nested ones like picklists.shipments.created.

Monitor hook status and reactivate

Watch for deactivation and use the reactivate endpoint, then reconcile anything missed while the hook was off.

Dedupe and respect rate limits

Dedupe on a payload identifier, make side effects idempotent, and back off on 429s when making follow-up API reads.

Make Picqer webhooks production-ready

Hookdeck verifies X-Picqer-Signature, keeps hooks from deactivating, deduplicates, and durably queues events

Conclusion

Picqer webhooks give warehouse and order activity a real-time push channel, but the reliability details matter: verification only exists if you set a per-hook secret, the signature is base64 (not hex) over the whole body, and a hook auto-deactivates after 5 complete failures in 24 hours. A missed secret or a flaky endpoint quietly undermines the integration.

That puts verification, fast acknowledgement, hook-health monitoring, and deduplication on you. Hookdeck verifies the signature, keeps hooks active by absorbing downstream failures, deduplicates, and durably queues every event at the edge, so your integration processes verified, unique events and never loses data to a deactivated hook.

Get started with Hookdeck for free and handle Picqer webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.