Die Entwicklerdokumentation ist auf Englisch verfügbar.
Webhook events
Register an HTTPS endpoint, choose delivery events, and read the signed event envelope Maxmove sends for every delivery change.
Webhooks tell your system about every delivery change as it happens, so you don't need to poll. Maxmove sends a signed POST with a JSON body to your endpoint for each event.
Register an endpoint#
Register a public HTTPS URL with POST /v1/webhooks. The key needs the webhooks:manage permission. List the events you want in events, or leave it empty to receive all of them.
curl -X POST https://api.maxmove.com/v1/webhooks \
-H "x-api-key: $MAXMOVE_KEY" -H "content-type: application/json" \
-d '{
"url": "https://shop.example.com/maxmove/webhooks",
"events": ["delivery.status_updated", "delivery.delivered", "delivery.cancelled"]
}'const res = await fetch('https://api.maxmove.com/v1/webhooks', {
method: 'POST',
headers: { 'x-api-key': process.env.MAXMOVE_KEY!, 'content-type': 'application/json' },
body: JSON.stringify({
url: 'https://shop.example.com/maxmove/webhooks',
events: ['delivery.status_updated', 'delivery.delivered', 'delivery.cancelled'],
}),
});
const endpoint = await res.json();
// Store endpoint.secret now. It is not shown again.import os
import requests
endpoint = requests.post(
"https://api.maxmove.com/v1/webhooks",
headers={"x-api-key": os.environ["MAXMOVE_KEY"]},
json={
"url": "https://shop.example.com/maxmove/webhooks",
"events": ["delivery.status_updated", "delivery.delivered", "delivery.cancelled"],
},
).json()
# Store endpoint["secret"] now. It is not shown again.The response contains the endpoint id (we_…) and the signing secret (whsec_…). The secret is shown only in this response. Store it to verify signatures.
- The URL must be a public HTTPS URL. Private and reserved network targets are rejected with
400 invalid_webhook_url. - Endpoints belong to the mode of the key that registered them. An endpoint registered with a test key only receives test deliveries, and the other way round.
- A workspace can register a limited number of endpoints per mode. Above that, registration answers
409 endpoint_limit_reached.
Events#
| Event | Sent when |
|---|---|
delivery.created | The delivery was created. data.status is pending. |
delivery.status_updated | The status changed, for example to picked_up or in_transit. |
delivery.courier_assigned | A courier accepted the delivery. |
delivery.courier_arrived | The courier arrived at a waypoint: data.status is at_pickup or at_dropoff. At an intermediate stop, the status doesn't change. |
delivery.eta_updated | The schedule or the route changed, so the expected arrival moved. |
delivery.pod_submitted | The courier submitted proof at pickup or dropoff. |
delivery.delivered | The delivery was completed. delivered_at is set. |
delivery.cancelled | The delivery was cancelled by you or by Maxmove, also when no courier was found. cancelled_at is set. |
Events are sent for deliveries created with POST /v1/deliveries. Fleet orders don't send webhook events.
The API reference lists the request Maxmove sends for each event type.
How to read events#
- One event per status change. Each forward status change produces one event.
delivery.eta_updated,delivery.pod_submitted, anddelivery.courier_arrivedat intermediate stops don't change the status. - Always read
data.status. Statuses that have their own event (courier_assigned,at_pickup,at_dropoff,delivered,cancelled) can also arrive asdelivery.status_updated, depending on how the underlying updates are ordered. Usedata.statusfrom any event as the current status, rather than the event type. delivery.courier_arrivedwithout a status change. At intermediate stops, the courier's arrival is reported, butdata.statusstays the same.delivery.eta_updatedcarries no ETA. It tells you that the schedule or route changed. Read the new ETA fromGET /v1/deliveries/{deliveryId}/tracking.delivery.pod_submittedper checkpoint. It is sent once for proof at pickup and once for proof at dropoff, so up to twice per delivery. Fetch the files fromGET /v1/deliveries/{deliveryId}/proof-of-delivery.
Event body#
{
"id": "evt_4c1f9e2b7a3d8c5e6f0a1b2c3d4e5f60",
"type": "delivery.status_updated",
"version": "2026-10",
"created_at": "2026-10-07T09:42:11Z",
"data": { "id": "del_…", "status": "picked_up", "pickup": { … }, "dropoff": { … }, … }
}data is the delivery as returned by GET /v1/deliveries/{deliveryId}, without the courier's phone number and position: data.courier carries only the courier's name.
version changes only with breaking payload changes. Additive changes, such as new fields in data, keep the version. Ignore fields you don't know. See Versioning.
Test your endpoint#
POST /v1/webhooks/{webhookId}/test sends a signed sample delivery.status_updated event to the endpoint. Its data.id is del_sample and data.test is true, so your handler can recognize and ignore it. Disabled endpoints answer 409 endpoint_disabled.
With a test key, every test delivery also sends real events as the Robocourier moves it along.
Manage endpoints#
| Task | Request |
|---|---|
| List endpoints | GET /v1/webhooks |
| Change URL, events, or description | PATCH /v1/webhooks/{webhookId} |
| Pause or resume | PATCH with "status": "disabled" or "status": "active" |
| Rotate the signing secret | PATCH with "rotate_secret": true |
| Delete an endpoint | DELETE /v1/webhooks/{webhookId} |
A disabled endpoint receives no new events. See Retries and replay for events that were already queued.
Hat das Ihre Frage beantwortet?