Skip to content

Delivery lifecycle

Every delivery moves through a normalized status model. Your integration should key off status on API responses and eventType on webhooks.

Status definitions

StatusTerminal?Description
acceptedNoCreated; dispatch in progress
dispatch_failedNo*No driver assigned — may recover or expire
scheduledNoWaiting for scheduled window
reassigningNoBeing re-dispatched to another driver
driver_assignedNoDriver assigned
driver_enroute_to_pickupNoDriver en route to pickup
driver_at_pickupNoDriver at pickup location
pickup_completeNoItems picked up
driver_enroute_to_dropoffNoDriver en route to customer
driver_at_dropoffNoDriver at customer location
deliveredYesSuccessfully completed
failedYesDelivery failed
return_in_progressNoReturning to store
returnedYesReturned to store
cancelledYesCancelled by client or operations
expiredYesTimed out without completion

* dispatch_failed becomes terminal only if followed by expired or cancelled.

Why a delivery ended that way

status tells you what happened; statusReasonCode tells you why, in a form you can branch on. It accompanies failed, cancelled, dispatch_failed, expired, and return_in_progress. The prose statusReason is for humans and may change without notice.

The codes worth handling explicitly:

GroupCodes
Recipientrecipient_unavailable, recipient_refused, recipient_not_at_cq
Addressaddress_not_found, address_inaccessible, unsupported_destination_type
Installation accessdenied_at_gate, credential_expired, credential_not_valid_for_installation, gate_closed, fpcon_elevated, escort_unavailable
Verificationverification_failed, age_verification_failed, photography_prohibited_no_substitute
Packagepackage_damaged, package_not_ready
Dispatchno_carrier_available, outside_service_area, quote_expired
Cancellation attributioncancelled_by_merchant, cancelled_by_recipient, cancelled_by_carrier, cancelled_by_onpoint
Externalweather_or_road_closure

Treat the access codes differently from recipient_unavailable. A driver turned away at a gate is not a customer who missed the door: the package never reached the destination, the recipient did nothing wrong, and the fix is credential or scheduling work rather than a redelivery attempt. Most delivery APIs collapse both into one "delivery attempt failed" bucket, which makes the failure rate uninterpretable and gives support nothing to route on. Retrying a denied_at_gate order unchanged will simply fail again.

Cancellation attribution matters for the same reason — cancelled_by_carrier is an OnPoint problem, cancelled_by_recipient is not, and only one of them should count against your fulfillment metrics.

Typical happy path

accepted
  → driver_assigned
  → driver_enroute_to_pickup
  → driver_at_pickup
  → pickup_complete
  → driver_enroute_to_dropoff
  → driver_at_dropoff
  → delivered

OnPoint guarantees that a delivery reaching delivered has passed through the intermediate driver milestones in order. If an upstream carrier omits a milestone, OnPoint infers it before emitting webhooks.

Special cases

Reassigning

reassigning means the delivery is still live. A new driver_assigned follows. Do not mark the order cancelled or create a replacement delivery unless your business rules require it.

Dispatch failed

The delivery record exists. You may:

  • Wait for automatic retry (if configured for your account)
  • Call PATCH /deliveries/{deliveryId} to update and trigger re-dispatch
  • Cancel via POST /deliveries/{deliveryId}/cancel

Returns

When capabilities.returns is true, failed or refused drop-offs may enter:

failed or driver_at_dropoff
  → return_in_progress
  → returned

Update eligibility

Lifecycle stageUpdatable fields
Before pickup_completeAll create fields (pickup, dropoff, windows, package, verification, handling, tip, metadata)
After pickup_complete, before delivereddropoff.instructions, dropoff.contactPhone, tipAmountCents
After deliveredNone — returns 409

Verification can be tightened but never loosened. Removing a check that cargo attributes or destination policy require returns 422 — you cannot drop the age check off an alcohol order mid-flight.

Cancellation eligibility

Cancellations are accepted until the delivery reaches a terminal state. Cancellation fees (when supported) appear in pricing.cancellationFeeCents on the response.

Polling vs webhooks

Use caseRecommendation
Live order tracking UIWebhooks + occasional GET for reconciliation
Checkout serviceabilityPOST /serviceability
Checkout quotePOST /quotes
Recovery after a webhook outageGET /events?delivered=false&since=
Recovery after your own outageGET /deliveries?from={lastSeenTimestamp}
Look up one order by your own IDGET /deliveries?clientOrderReference={ref}
Nightly reconciliationGET /deliveries?from=&to=

Prefer GET /events?delivered=false for webhook gaps. It returns exactly the events your endpoint never received, in order, with payloads identical to the originals — so you replay them through the same handler instead of diffing delivery state and inferring what you missed.