Drop Docs

Webhooks

Order and delivery state changes are asynchronous. Configure a webhook endpoint, verify the Drop signature, and process Drop lifecycle events idempotently.

Outbound webhooks are automatic. Once a merchant has an active webhook endpoint configured, Drop sends events to that endpoint whenever state changes happen inside Drop. No manual trigger is required for normal event delivery.

order.created

Order request accepted by Drop.

order.driver_assigned

A driver accepted the assignment.

order.arrived_pickup

Driver reached the pickup point.

order.picked_up

Driver has collected the package.

order.in_transit

Order is on the way to the customer.

order.delivered

Delivery completed successfully.

order.failed

Delivery failed and requires reconciliation.

order.cancelled

Order cancelled before completion.

Example webhook payload

json
{
  "id": "evt_8d2e5db7-4d93-4b8c-a6d3-2d96d3a1f2c7",
  "type": "order.in_transit",
  "created_at": "2026-04-12T10:15:22.431Z",
  "data": {
    "order_id": "cmnuz0bc80004nz01purkbm81",
    "reference": "ORD-2026-1042",
    "merchant_id": "cmngd6cjl0000ni01nt15bfeb",
    "store_id": "cmngd6clq0002ni01juetjbc1",
    "order_status": "IN_TRANSIT",
    "delivery_id": "cmnuz0bc80005nz01x1j2a3b4",
    "delivery_status": "IN_TRANSIT",
    "tracking_url": "https://dropsa.co.za/track/cmnuz0bc80007nz01cwr1kqj8",
    "driver": {
      "id": "drv_01",
      "first_name": "Lerato",
      "last_name": "Mokoena",
      "phone": "+27825550123"
    },
    "customer": {
      "id": "cus_01",
      "name": "Lebo Ndlovu",
      "phone": "+27825550124"
    },
    "customer_note": "Please call when outside.",
    "proof_of_delivery": null,
    "driver_id": "drv_01"
  }
}

Signature header

text
x-drop-signature: t=1711111111,v1=<hex_hmac_signature>

Node.js signature verification

js
import crypto from "node:crypto";

const payload = rawBody;
const header = req.headers["x-drop-signature"];
const secret = process.env.DROP_WEBHOOK_SECRET;

const [timestampPart, signaturePart] = header.split(",");
const timestamp = timestampPart.split("=")[1];
const signature = signaturePart.split("=")[1];

const expected = crypto
  .createHmac("sha256", secret)
  .update(`${timestamp}.${payload}`)
  .digest("hex");

if (expected !== signature) {
  throw new Error("Invalid webhook signature");
}