Die Entwicklerdokumentation ist auf Englisch verfügbar.
Test webhooks locally
Receive Maxmove webhooks on your laptop through a tunnel, trigger every event with a test key, and replay failed events.
Maxmove only sends webhooks to public HTTPS URLs. Private, local, and reserved addresses such as localhost or 192.168.x.x are rejected, both when you register the endpoint and before every send. To test on your machine, put a tunnel in front of your local server.
Run a local receiver
Read the raw body before parsing it: the signature is computed over the exact bytes.
import { createServer } from 'node:http'; createServer((req, res) => { const chunks: Buffer[] = []; req.on('data', (chunk) => chunks.push(chunk)); req.on('end', () => { const rawBody = Buffer.concat(chunks); console.log(req.headers['webhook-id'], req.headers['webhook-signature']); console.log(rawBody.toString('utf8')); res.writeHead(200).end(); }); }).listen(4242);from flask import Flask, request app = Flask(__name__) @app.post("/maxmove/webhooks") def maxmove_webhook(): raw_body = request.get_data() # exact bytes, for signature checks print(request.headers.get("webhook-id"), request.headers.get("webhook-signature")) print(raw_body.decode("utf-8")) return "", 200 app.run(port=4242)Open a tunnel
Any HTTPS tunnel works, for example Cloudflare's quick tunnel or ngrok:
cloudflared tunnel --url http://localhost:4242Note the public
https://…URL it prints.Register the endpoint with your test key
Register the tunnel URL with
POST /v1/webhooksusing anmm_test_…key. Store thesecretfrom the response: it is shown only once. An endpoint registered with a test key receives events of test deliveries only.curl -X POST https://api.maxmove.com/v1/webhooks \ -H "x-api-key: $MAXMOVE_TEST_KEY" -H "content-type: application/json" \ -d '{ "url": "https://your-tunnel.trycloudflare.com/maxmove/webhooks" }'Send a sample event
POST /v1/webhooks/{webhookId}/testsends one signeddelivery.status_updatedevent withdata.test: true. Use it to check your signature verification.Run a full lifecycle
Create a delivery with the test key. The Robocourier moves it through every status, about 20 seconds per step, and you receive the same signed events as for a live delivery. Add
test_scenario(courier_cancelledordelivery_failed) to get the failure paths; see Cancellations.
When your receiver was down#
Maxmove retries failed sends up to 8 times with growing intervals, at most 15 minutes apart. After that the events are kept as failed. Once your receiver is back, POST /v1/webhooks/{webhookId}/replay sends all of them again. See Retries.
Quick tunnel URLs change on every start. Update the endpoint with PATCH /v1/webhooks/{webhookId} instead of registering a new one each time, or delete old endpoints: a workspace can have at most 10 per mode (live and test).
Example payloads#
One example per event type, for a live delivery from Köln to Düsseldorf. data is the delivery as returned by GET /v1/deliveries/{deliveryId}, with courier reduced to the courier's name. Read data.status rather than the event type alone: events can arrive out of order, and a status change can arrive as delivery.status_updated.
{
"id": "evt_014c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.created",
"version": "2026-10",
"created_at": "2026-10-07T09:30:00Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "pending",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": null,
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T09:30:00Z",
"cancelled_at": null,
"delivered_at": null
}
}{
"id": "evt_024c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.courier_assigned",
"version": "2026-10",
"created_at": "2026-10-07T09:32:10Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "courier_assigned",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": {
"name": "Jonas"
},
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T09:32:10Z",
"cancelled_at": null,
"delivered_at": null
}
}{
"id": "evt_034c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.courier_arrived",
"version": "2026-10",
"created_at": "2026-10-07T09:48:05Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "at_pickup",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": {
"name": "Jonas"
},
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T09:48:05Z",
"cancelled_at": null,
"delivered_at": null
}
}{
"id": "evt_044c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.status_updated",
"version": "2026-10",
"created_at": "2026-10-07T09:52:40Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "picked_up",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": {
"name": "Jonas"
},
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T09:52:40Z",
"cancelled_at": null,
"delivered_at": null
}
}{
"id": "evt_054c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.eta_updated",
"version": "2026-10",
"created_at": "2026-10-07T10:05:12Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "in_transit",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": {
"name": "Jonas"
},
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T10:05:12Z",
"cancelled_at": null,
"delivered_at": null
}
}{
"id": "evt_064c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.pod_submitted",
"version": "2026-10",
"created_at": "2026-10-07T10:31:58Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "at_dropoff",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": {
"name": "Jonas"
},
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T10:31:58Z",
"cancelled_at": null,
"delivered_at": null
}
}{
"id": "evt_074c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.delivered",
"version": "2026-10",
"created_at": "2026-10-07T10:33:20Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "delivered",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": {
"name": "Jonas"
},
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T10:33:20Z",
"cancelled_at": null,
"delivered_at": "2026-10-07T10:33:20Z"
}
}{
"id": "evt_084c1f9e2b7a3d8c5e6f0a1b2c3d4e5f",
"type": "delivery.cancelled",
"version": "2026-10",
"created_at": "2026-10-07T09:31:15Z",
"data": {
"id": "del_0c9b0f6e2d8a4e1f9b7c5a3d1e2f4a6b",
"status": "cancelled",
"mode": "live",
"external_id": "order-4711",
"quote_id": "qt_8f14e45fceea167a5a36dedd4bea2543",
"amount_cents": 4890,
"currency": "EUR",
"pickup": {
"address": "Ehrenstraße 15, 50672 Köln",
"latitude": 50.9385,
"longitude": 6.9469,
"contact": {
"name": "Feinkost Ehrenfeld",
"phone": "+49 221 1234567"
},
"instructions": "Pick up at the back entrance."
},
"dropoff": {
"address": "Königsallee 27, 40212 Düsseldorf",
"latitude": 51.2233,
"longitude": 6.7768,
"contact": {
"name": "Erika Muster",
"phone": "+49 170 1234567"
},
"instructions": "Ring at reception, 2nd floor."
},
"stops": [],
"vehicle_type_id": "courier",
"notes": null,
"scheduled_at": null,
"tracking_available_at": null,
"tracking_url": "https://maxmove.com/de/track/3J8kQ2pVx9",
"courier": null,
"created_at": "2026-10-07T09:30:00Z",
"updated_at": "2026-10-07T09:31:15Z",
"cancelled_at": "2026-10-07T09:31:15Z",
"delivered_at": null
}
}Hat das Ihre Frage beantwortet?