Cancel a pickup
/api/v1/pickups/{pickup_id}/cancel idempotentCancel one pickup through the same policy as the web portal: allowed within one hour of arranging it, not once the driver has collected it, not while a carrier swap holds it. Cancels a single pickup, never the whole arrangement; cancel each pickup of a booking separately. No request body. Idempotency-Key is optional: cancelling is naturally idempotent, and cancelling an already cancelled pickup answers 200 with already_cancelled: true (it also re-attempts a refund that has not landed yet). Repeating a request with the same Idempotency-Key while the key is stored returns the first response unchanged (including its refund block) and does not look at the pickup again; a request without a key, or with a new key, re-evaluates the pickup and retries a pending refund. Requires scope pickups:cancel. Refused with 409: PICKUP_CANCEL_WINDOW_EXPIRED (cancel_window_expired), PICKUP_NOT_CANCELLABLE (reason_code pickup_in_progress, already_picked_up, swap_in_progress or env_mismatch; the last means the pickup was placed on a different carrier environment than this server uses, which only operations can resolve, so contact customer service; a 409 already cached under an Idempotency-Key is not invalidated automatically, so after operations confirms the fix retry without a key or with a new one) and PICKUP_STATE_CONFLICT (pickup_state_changed, the pickup changed while it was being cancelled: read it with GET /v1/pickups/{pickup_id} and decide again). 502 PICKUP_CANCEL_UPSTREAM_FAILED has two causes, told apart by data.reason_code: external_side_effects_disabled (carrier calls are switched off on our side: no carrier call was made and nothing changed) and fuuffy_cancel_failed (the carrier side could not confirm the cancellation: the pickup is not marked cancelled on our side, but the carrier outcome may be unconfirmed, since the request may or may not have reached it). A 502 is never cached, so repeating the request is safe. This differs from 503 PICKUP_FEATURE_DISABLED, which means the pickup API gate refused the request before any cancellation was attempted. Another account's pickup and an unknown id answer 404. refund.state reports the money: not_applicable (nothing was charged, or the fee still covers other live pickups of the same address group or manifest), pending (charged, no pickup of that fee is left and the refund is not recorded yet; cancelling again retries it), refunded (refund.hkd is the amount; also reported when the wallet ledger already holds the refund although the fee block carries no refund marker) or not_refundable (charged, and by policy never refunded because the driver had already collected the pickup; retrying changes nothing). Fees are shared per address group, so cancelling one pickup of a group with other live pickups refunds nothing.
Parameters
Responses
{ "status": 200, "message": "OK", "data": { "pickup": { "id": "6710a1b2c3d4e5f607182960", "livemode": false, "simulated": true, "manifest_id": null, "address_group_id": "6710a1b2c3d4e5f607182951", "status": "cancelled", "destination": "yunexpress", "parcel_carriers": [ "yunexpress" ], "shipment_ids": [ "6710a1b2c3d4e5f607182931" ], "tracking_numbers": [ "IM2610190000000001" ], "pickup_date": "2026-10-21", "scheduled_pickup_date": "2026-10-21", "pickup_window": { "start": "13:00", "end": "17:00", "timezone": "Asia/Hong_Kong" }, "pickup_address": { "name": "Chan Tai Man", "phone": "+852 91234567", "email": "ops@example.com", "address_line": "Unit 1203, 12/F, Tower 2, Kowloon Commerce Centre, 51 Kwai Cheong Road, Kwai Chung" }, "carrier_tracking_number": null, "pickup_courier": null, "failure_reason": null, "fee": { "scope": "address_group", "state": "provisional", "estimated_hkd": 0, "charged_hkd": 0, "refunded_hkd": 0 }, "cancellation": { "allowed": false, "deadline": "2026-10-19T04:12:40.123Z", "reason_code": "already_cancelled" }, "created_at": "2026-10-19T03:12:40.123Z", "updated_at": "2026-10-19T03:20:05.881Z" }, "already_cancelled": false, "refund": { "state": "not_applicable", "hkd": null } } }
Returned object
| Field | Type | Sample |
|---|---|---|
| status | number | 200 |
| message | string | OK |
| data | object | {3 keys} |