Webhooks
Webhooks let your external systems receive notifications when events occur in your OpenBoxes Lift account. Instead of polling the API for changes, you register a webhook URL and Lift pushes signed event data to your endpoint.
Rolling out: Webhook management is live, and automatic delivery is switched on for eleven event types. From your OpenBoxes instance, typically within a couple of minutes:
order.created,order.approved,order.received,order.cancelled,order.updated,shipment.created,shipment.shipped, andinventory.stock_adjustment. Platform events:tenant.provisioned,tenant.suspended,tenant.reactivated. Billing and data-import events are built and being switched on in stages. Stock-level changes, low-stock alerts and expiry warnings are not available and have no announced date — for those, poll the REST API. The event list in the Lift portal is the source of truth for what delivers today; a type you can subscribe to there is live, and anything else is not yet. You can also send signed test deliveries at any time to exercise your receiver end-to-end.
How Webhooks Work
- You register a webhook URL in the Lift portal (Webhooks in the left navigation)
- When a subscribed event occurs, Lift sends an HTTP POST to your URL
- Your endpoint processes the payload and returns a
2xxresponse - If delivery fails, Lift retries on a fixed backoff schedule (see below)
OpenBoxes Lift Your Server
│ │
│ Event occurs │
│ │
├──── POST /your/webhook/url ───>│
│ { id, type, created, │
│ data } │
│ │
│<──── 200 OK ──────────────────│
│ │
Delivery Semantics
Once an event is accepted by the webhook pipeline, delivery to each subscribed endpoint is at-least-once: every event carries a stable id, and your consumer should deduplicate on it, because the same event can be delivered more than once (for example around a retry or a crash boundary). Events may occasionally be dropped before acceptance — emission never blocks or fails the business action that caused it.
Webhooks are a notification channel, not a ledger. Use them to react promptly, and reconcile authoritative state against the REST API rather than reconstructing it from webhook history.
Event Types
The event types you can subscribe to are listed on the Webhooks page in the portal (the create dialog and the GET /api/v1/webhooks/event-types endpoint read the same registry). During the rollout, being subscribed to an event type does not yet mean it delivers — emission is being enabled per event type, and a subscribed endpoint starts receiving an event type once emission for it goes live.
Payload Format
Every delivery wraps the event in the same envelope:
{
"id": "evt-abc123def456",
"type": "tenant.provisioned",
"created": 1753488000,
"data": {
"tenantId": "acme",
"status": "ACTIVE"
}
}
Payload Fields
| Field | Type | Description |
|---|---|---|
id |
string | Stable event ID — deduplicate on this |
type |
string | Event type identifier |
created |
number | Unix timestamp (seconds) when the delivery was sent |
data |
object | Event-specific data (the affected resource) |
Configuring Webhooks
Webhooks are managed in the Lift portal under Webhooks (left navigation), not in account settings.
| Setting | Description |
|---|---|
| URL | Endpoint to receive webhook POST requests — use HTTPS |
| Events | Which event types to subscribe to |
| Secret | Generated whsec_… secret used to sign every delivery (regenerate any time) |
| Custom headers | Optional headers added to every delivery |
| Active | Enable or disable the webhook |
Requests to private or internal network addresses are blocked. Always use HTTPS endpoints; plain http:// URLs are deprecated and will stop being accepted.
Signature Verification
Every delivery includes two headers:
X-OpenBoxes-Signature: v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-OpenBoxes-Timestamp: 1753488000
The signature is HMAC-SHA256, computed over the string "<timestamp>.<raw request body>" using your webhook secret, hex-encoded, and prefixed with v1=.
To verify:
- Read the
X-OpenBoxes-Timestampheader value - Concatenate it with the raw request body, separated by a single
. - Compute HMAC-SHA256 of that string with your
whsec_…secret - Hex-encode the result, prefix with
v1=, and compare withX-OpenBoxes-Signatureusing a constant-time comparison - Reject requests whose timestamp is more than 5 minutes from your current time (replay protection)
# Example: verify a webhook signature
signed_payload="${TIMESTAMP}.${REQUEST_BODY}"
expected="v1=$(echo -n "$signed_payload" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | cut -d' ' -f2)"
if [ "$expected" = "$RECEIVED_SIGNATURE" ]; then
echo "Signature valid"
fi
Retry Policy
If your endpoint does not return a 2xx response, Lift retries delivery on a fixed schedule:
| Attempt | Delay after previous failure |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 15 minutes |
| 4th retry | 1 hour |
| 5th retry | 4 hours |
The delays are minimums: retries are picked up by a background scheduler, so an attempt can run slightly later than the scheduled time. After 5 failed retries the delivery is marked exhausted. Delivery history — including status, HTTP response codes, and timing — is visible per webhook in the portal.
Testing Your Endpoint
Use Send Test Event on a webhook in the portal to trigger a fully signed delivery to your endpoint — the same code path used for real events. You can also send a realistic sample of any active event type. A successful test proves your endpoint is reachable and returning 2xx, and gives you a genuine signed request to develop and check your signature verification against.
No public endpoint yet? Create a test receiver on the portal's Webhooks page: a URL you can register a webhook against, whose deliveries are captured in the portal — signed exactly like production — instead of leaving the platform. Receivers expire after 24 hours.
Manual retry: failed or exhausted deliveries can be retried from the delivery log. Each retry is a new, freshly signed attempt (so your replay-protection window accepts it), linked to the original in the log.
Best Practices
- Return 2xx quickly: Process webhook payloads asynchronously. Acknowledge immediately and handle the event in a background job.
- Handle duplicates: Use the
idfield to deduplicate. At-least-once delivery means the same event can arrive more than once. - Verify signatures: Always validate
X-OpenBoxes-Signature(with the timestamp check) so you only act on requests that genuinely came from Lift. - Use HTTPS: Terminate TLS at your receiver; internal/private addresses are rejected.
- Monitor failures: Check the delivery history in the portal and investigate endpoint availability issues promptly.