API reference
Partner events
Report lifecycle events for the packages you handle — as a carrier, PUDO point, customs broker or a foreign Starmile organization on the next leg. Each event you send moves the package forward in its flow. We validate every event before accepting it, so a doomed update is rejected immediately with a reason and a fix.
One event per status the package reaches
202 with result: "applied" and the new order_status. A rejected event returns 422 with result, an error and a hint. Requires an event scope (events:transport, events:pudo, events:customs or leg:handoff).You send the events your scopesallow (see below). The endpoint defines the full menu; you use the subset you are granted. If a step in a package’s flow needs an event you do not send, the package simply does not move forward.
Validate before accept
Before an event is accepted, it is checked synchronously. Only a valid event is enqueued for the destination — we never accept an update that cannot be applied:
- Scope — your credential is allowed to send this event type.
- Resolve — the package and the destination organization (tenant) that owns it are found.
- Step order— the event is a valid next transition for the package’s current status in its flow (an out-of-order event, e.g.
deliveredbeforeout_for_delivery, is rejected). - Smoke test — we dry-run the transition the destination would apply (guards pass, target step exists, context present).
- On failure →
422with anerror(what is wrong) and ahint(how to fix). On success → the event is routed to the destination organization, which advances the package.
Delivery is idempotent: re-sending the same event_id is safe and applied once.
Report an event
/api/v1/partner/eventsBody
event_idstringrequired- Your unique id for this event — the idempotency key. Re-sending it is safe.
typestringrequired- The event type, from the catalogue below. Must be within your granted scope.
tracking_numberstringrequired- The Starmile tracking number (or parcel / bag reference) the event applies to.
occurred_atstringrequired- ISO-8601 timestamp of when the event actually happened.
dataobjectoptional- Optional event-specific payload. Its allowed fields are fixed per event type (see the catalogue below) and type-checked; every event also accepts a free-text note. Any field the event does not define is rejected 422.
curl -X POST https://api.starmile.io/api/v1/partner/events \
-H 'Authorization: Bearer <access_token>' \
-H 'Content-Type: application/json' \
-d '{
"event_id": "evt-7f3a-0001",
"type": "shipment.out_for_delivery",
"tracking_number": "STM000123",
"occurred_at": "2026-06-20T15:02:00Z",
"data": {
"driver": "Rashad",
"eta": "2026-06-20T17:30:00Z"
}
}'Scopes
Access is granted as scopes on your credential — not by a partner “type”. You may hold any combination; a party that is both a carrier and a PUDO simply gets both event scopes on one credential. An event outside your granted scope is rejected 422.
Event catalogue
The events the platform models today, grouped by the scope that grants them. Each is accepted only when its status is a legal next step for the package — otherwise you get a 422 listing the statuses expected next. The data fields column lists exactly what each event’s data object may carry — all optional and type-checked; every event also accepts a free-text note, and any other field is rejected.
events:transport — carrier
| Event | Sets status | data fields | Description |
|---|---|---|---|
shipment.accepted_by_carrier | customs_released | notecarrier_referenceawb | The carrier has taken custody of the shipment for the line haul. |
shipment.arrived_at_terminal | arrived_at_terminal | noteterminal | The shipment reached the destination-country terminal. |
shipment.out_for_delivery | out_for_delivery | notedrivereta | A driver is carrying the parcel to the recipient. |
shipment.delivered | delivered | noterecipient_namesigned_byproof_of_delivery | Handed over to the recipient — the delivery is complete. |
shipment.delivery_failed | delivery_failed | notereasonattempt | A delivery attempt did not succeed (e.g. recipient absent). |
shipment.returned | returned | notereason | The shipment is being returned to origin. |
events:pudo — pick-up / drop-off point
| Event | Sets status | data fields | Description |
|---|---|---|---|
parcel.accepted | accepted_at_pudo | notepoint_codeshelf | Accepted at the PUDO point — for a delivery it is now held for the recipient to collect; for a return it is held to go back. The hold window follows the order's service direction. |
parcel.ready_for_driver | parcel_racked_at_hub | notepoint_code | Staged and ready for your driver to collect from the point. |
parcel.received | received_at_pudo | notepoint_code | Parcel received at the PUDO point. |
parcel.delivered | delivered | noterecipient_namesigned_byproof_of_delivery | Handed over to the recipient at the point. |
parcel.timeout | undelivered | note | Uncollected past the point's storage window. |
parcel.dropoff_cancelled | dropoff_cancelled | notereason | A scheduled drop-off at the point was cancelled. |
events:customs — customs broker (not on Smart Customs)
| Event | Sets status | data fields | Description |
|---|---|---|---|
customs.held | customs_hold | notereasonreference | Customs placed the parcel on hold pending review or documents. |
leg:handoff — foreign Starmile organization on the next leg
| Event | Sets status | data fields | Description |
|---|---|---|---|
leg.received | received_at_hub | notereference | The destination organization received the package for its leg. |
leg.out_for_delivery | out_for_delivery | notedrivereta | Out for delivery on the destination organization's leg. |
leg.delivered | delivered | noterecipient_namesigned_byproof_of_delivery | Delivered on the destination organization's leg. |
leg.delivery_failed | delivery_failed | notereasonattempt | A delivery attempt on the destination leg did not succeed. |
leg.returned | returned | notereason | The package is being returned on the destination leg. |