Configuration
Configure your webhook URL in the Zinc dashboard under Settings. You can also generate a webhook secret for signature verification.Webhook Secret
Your webhook secret is used to verify that incoming webhook requests are from Zinc. The secret format is:Events
Order events
Return events
Return events carry an additionalreturn_id field alongside order_id so you can route by return without parsing the data object. The status field on these events reflects the return-request status (not the order status).
Wallet events
Wallet events aren’t about an order, so they use a smaller payload with noorder_id, return_id, or top-level status. See Wallet event payloads.
Payload Structure
All webhook payloads follow this structure:Payload Fields
Event-Specific Data
order.placed includes price components:
order.failed includes error information:
order.tracking_received, order.shipped, order.available_for_pickup, order.delivered) all carry data.tracking_numbers. Each entry has:
On these events the top-level
status is the order’s status, which stays order_placed once the order is placed. Shipment progress is reported by the event itself and by each tracking number’s status.
order.tracking_received includes the tracking numbers we just received:
order.shipped fires once per order, when the first package starts moving through the carrier network. It lists every tracking number on the order so far:
order.estimated_delivery_updated fires when the retailer revises a delivery estimate you’ve already been given, whether it slips or moves earlier. The first estimate on an order doesn’t fire it. Today it’s sent for Amazon Business orders only. estimated_delivery and previous_estimated_delivery are passed through as the retailer reports them: either a date (2026-01-19) or an ISO 8601 timestamp (2026-01-19T03:00:00Z):
order.available_for_pickup fires for one package each time it moves into available_for_pickup. A multi-package order can send several. available_at is when the package first became available in its current pickup run, and location is the carrier facility, when the carrier reports it. It isn’t sent when nothing on the order can still reach the shopper (for example, a cancelled order):
order.delivered fires when every package on the order has been delivered. Each tracking number adds delivered_at and delivery_proof_url (the carrier’s proof-of-delivery photo, or null). delivered_at may be missing when the retailer, rather than the carrier, reported the delivery:
order.cancelled fires when the retailer cancels the order after placement. The order’s wallet hold is refunded at the same time:
return.created fires when a customer files a return against one of your orders. The data object echoes the return reason, free-text notes, and the line items being returned:
return.label_uploaded fires when a printable mail-back label is attached to the return, which can happen before, at the same time as, or after approval. It fires again if the label is replaced. label_url is a signed link you can fetch straight from the payload. It stops working at label_url_expires_at (7 days), and it’s null if signing failed. In that case, fetch the label from GET /returns/{return_id}. kind is label_pdf or package_label:
return.approved fires when the return is approved. Where applicable, the data object carries the merchant-issued return id and a printable label URL so you can hand them to the customer:
return.denied fires when the return is denied (e.g. outside the retailer’s return window). merchant_return_id and external_label_url are null on denied returns:
return.credited fires when the return is resolved by crediting the customer’s wallet rather than working a merchant RMA. There’s no carrier label or merchant return id in this flow:
Wallet event payloads
Wallet events report the top-up amount in cents and its outcome.error is included on failed and disabled:
Security
Webhook requests include headers for verification:Verifying Signatures
To verify a webhook is from Zinc, compute the HMAC-SHA256 signature of the raw request body using your webhook secret and compare it to theX-Webhook-Signature header.
Python Example:
Always verify webhook signatures before processing the payload to ensure the request originated from Zinc.
Best Practices
- Respond quickly - Return a 2xx status code as soon as possible. Process the webhook asynchronously if needed.
-
Handle duplicates - Webhooks may occasionally be delivered more than once. Deduplicate on
eventtogether withorder_id(orreturn_idfor return events). Wallet events have neither, so useeventandtimestamp. -
Verify signatures - Always validate the
X-Webhook-Signatureheader before trusting the payload. - Use HTTPS - Configure an HTTPS endpoint to ensure webhook data is encrypted in transit.
- Log events - Keep records of received webhooks for debugging and auditing.

