Webhook Events & Verification
Webhook Events & Verification
Register a webhook endpoint to receive real-time delivery status updates instead of polling.
Setup
The response includes a signing secret (prefixed alwh_). Store it securely. It is only shown once. Subsequent GET /dd/v1/webhooks responses return the secret masked as alwh_****.... If you need to rotate your secret later, call PUT /dd/v1/webhooks with { "rotate_secret": true } and the new secret is returned in full.
One webhook URL per organization
Each Autolane organization has a single webhook subscription. Re-registering with POST /dd/v1/webhooks while one already exists returns 409 WEBHOOK_EXISTS; use PUT /dd/v1/webhooks to update the URL or subscribed events instead. If you operate multiple sites under one organization, all of their delivery events arrive at the same URL. Note that external_delivery_id is unique per (retailer, external_delivery_id) in the API, so two sites under the same org could legitimately use the same external_delivery_id value for different deliveries; ensure your IDs are org-unique (for example, prefix them with your site identifier) before routing on that field alone. DSP aggregators that bridge multiple retailer organizations should register one webhook per partner organization’s API key.
Events
Subscribe to additional events at any time by calling PUT /dd/v1/webhooks with an updated events array.
Payloads
When an event fires, Autolane sends a POST request to your registered URL with a JSON body.
pickup_status.changed
estimated_arrival is the estimated arrival time at the customer — the delivery ETA locked when the quote was created. It is null only when no delivery ETA is available for the delivery. On RETURN_IN_PROGRESS it is the estimated arrival back at the store instead; see Return fields.
If you are also subscribed to pickup.vehicle_location.changed, this event additionally carries a vehicle_location field with the vehicle’s position at the moment the status changed. The field is null when no fresh location fix (within the last 5 minutes) is available, and is omitted entirely when you are not subscribed to the location event.
stall_id (sites with stall reporting)
Some stores identify the stall the vehicle is parked in so staff know where to bring the order. Ask your Autolane contact to enable stall reporting for a site. When it is enabled:
data.stall_idis present onARRIVED_AT_STORE,WAITING_FOR_LOAD, andLOADEDevents. It is a short uppercase string such asA4orB-12.- It is
nullon those statuses when no stall was recorded for the arrival. - It is omitted entirely on every other status, and omitted for sites without stall reporting.
- If the stall changes while the delivery is at
ARRIVED_AT_STOREorWAITING_FOR_LOAD, you receive anotherpickup_status.changedevent withprevious_statusequal tostatusand the newstall_id. Treat the newest event bytimestampas authoritative. If you de-duplicate on(external_delivery_id, status), exempt events whoseprevious_statusequalsstatus. A delivery that is alreadyLOADEDgets no correction event; the GET endpoint still returns its current stall.
GET /dd/v1/deliveries/{external_delivery_id} returns the same stall_id under the same rule, so polling integrations see it too.
Return fields (sites with returns enabled)
When a site has returns enabled and a cancel lands at LOADED or later, the delivery reports RETURN_IN_PROGRESS, then RETURN_ARRIVED, then RETURNED, always in that order and never followed by EN_ROUTE_TO_CUSTOMER, ARRIVED_AT_CUSTOMER, or COMPLETED. Return events for one delivery are sent in order: RETURN_ARRIVED is not sent until RETURN_IN_PROGRESS was delivered or given up on, and the same holds for RETURNED. See Returns for the cancel matrix.
On the three return statuses data carries:
return_reason:CUSTOMER_CANCELLED,COLD_CHAIN_EXPIRED, orOTHER, normalized from the cancel’scancellation_reason(any unrecognized or non-string value becomesOTHER), ornullwhen the cancel carried none.return_stall_id(sites with stall reporting only):nulluntil a return stall is recorded, then a short uppercase string such asB21. Omitted for sites without stall reporting.stall_idkeeps the value from the pickup and never changes during a return.return_fee_centsonRETURNED: the flat per-site fee in cents, ornullwhen no fee is set.estimated_arrival: the estimated arrival back at the store onRETURN_IN_PROGRESS;nullonRETURN_ARRIVEDandRETURNED.vehicle_locationunder the same rules as every other status, throughRETURNED.
If the return stall changes while the delivery is RETURN_ARRIVED, you receive another pickup_status.changed with status and previous_status both RETURN_ARRIVED and the new return_stall_id. Treat the newest event by timestamp as authoritative, and exempt events whose previous_status equals status from (external_delivery_id, status) de-duplication, as for stall corrections.
Return starts (cancel received at EN_ROUTE_TO_CUSTOMER):
Back at the store, with a return stall:
The store took the order back:
customer and delivery_address are present as on every other status; the examples omit them for brevity. GET /dd/v1/deliveries/{external_delivery_id} returns return_reason, return_stall_id, and (once RETURNED) return_fee_cents under the same rules.
tracking_link.created
pickup.vehicle_location.changed
recorded_at is the timestamp on the underlying telemetry sample. It may lag the outer timestamp by a few seconds.
Events are not strictly ordered across event types. A pickup.vehicle_location.changed event may arrive shortly after a terminal pickup_status.changed (ARRIVED_AT_CUSTOMER, COMPLETED) for the same delivery. Use the timestamp field to reconcile relative ordering on your side.
Request headers
Each request includes these headers:
Your endpoint should return any 2xx status code to acknowledge receipt. Non-2xx responses (and request timeouts past 10 seconds) trigger retries.
Verifying Signatures
Use the signing secret from your webhook registration to verify that each delivery is authentically from Autolane. The X-Autolane-Signature header contains an HMAC-SHA256 hex digest of the raw request body, computed using your alwh_-prefixed secret.
Always verify signatures using a timing-safe comparison to prevent timing attacks:
Replay protection
The signature covers the request body only and does not include a timestamp. To reject replayed events at your endpoint, combine two payload-level signals:
- The top-level
timestampfield on every event is an ISO 8601 string. Reject events whosetimestampis more than a small tolerance (e.g., 5 minutes) outside your server clock. - The
X-Autolane-Delivery-Idheader is a UUID stable across retries for the same delivery attempt. Treat it as the dedupe key (see the next section).
Idempotency
Each delivery includes an X-Autolane-Delivery-Id header, a UUID that stays the same across retries. Store processed delivery IDs in a shared store (Redis, your database, etc.) and reject duplicates so retried deliveries don’t double-fire your business logic.
Note that external_delivery_id is the create-side idempotency key (see Idempotency in the Overview). It identifies the underlying delivery and stays the same across all events for that delivery. X-Autolane-Delivery-Id identifies one webhook event delivery (stable across the retries for that event) and changes from one event to the next.
Retries
Failed deliveries (non-2xx responses or request timeouts past 10 seconds) are retried with exponential backoff on a 30-second base:
After 5 failed attempts the event is given up on. Failed attempts also increment a per-organization consecutive_failures counter; a successful delivery resets it to zero.
If the counter reaches 50 consecutive failures the webhook endpoint is automatically disabled and the notification email on your account receives a heads-up. Call PUT /dd/v1/webhooks (with any valid update body, or just { "url": "<your-current-url>" }) to re-enable the endpoint. Re-enabling resets the failure counter to zero.