Appearance
Getting started
The OnPoint Delivery API lets you create deliveries, retrieve live status, manage cancellations, and receive real-time webhook notifications — without integrating directly with underlying carrier systems.
1. Obtain an API key
OnPoint issues a Bearer API key per merchant and environment (sandbox / production). Store it securely; never embed it in client-side code.
2. Choose your environment
| Environment | Purpose |
|---|---|
| Sandbox | Development and QA. No live drivers or customer SMS. |
| Production | Live deliveries. |
All requests use:
Authorization: Bearer YOUR_API_KEY3. Discover capabilities
Before building checkout or dispatch UI, call GET /capabilities. Your tenant may or may not support quotes, returns, live driver location streaming, or line-item package details.
bash
curl "https://api.sandbox.getonpoint.io/delivery/v1/capabilities" \
-H "Authorization: Bearer YOUR_API_KEY"Example response:
json
{
"accountType": "marketplace",
"capabilities": {
"quotes": true,
"marketplaceDispatch": true,
"ownedFleetDispatch": false,
"returns": true,
"requirementEnforcement": true,
"driverLocationStreaming": true,
"routeOptimization": false,
"itemLevelPackage": true,
"cancellationFees": true,
"maxRequestsPerSecond": 20
}
}4. Typical integration flow
mermaid
sequenceDiagram
participant App as Your application
participant OP as OnPoint API
participant WH as Your webhook endpoint
App->>OP: POST /serviceability (at address entry)
OP-->>App: serviceable + gate + permitted proof types
App->>OP: POST /quotes (optional)
OP-->>App: quoteId + price options
App->>OP: POST /deliveries
OP-->>App: deliveryId + status accepted
OP->>WH: delivery.driver_assigned
OP->>WH: delivery.driver_enroute_to_pickup
OP->>WH: delivery.delivered
App->>OP: GET /deliveries/{deliveryId}
OP-->>App: full delivery snapshotStep A0 — Check serviceability
Call POST /serviceability when the customer enters an address, before you price anything. It costs nothing and answers a question a quote cannot: whether this stop is reachable at all, right now.
bash
curl -X POST "https://api.sandbox.getonpoint.io/delivery/v1/serviceability" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dropoff":{"address":"2175 Reilly Rd, Fort Liberty, NC 28310","installation":{"code":"INST-FTLIBERTY"}}}'A serviceable: false result is a successful evaluation, not an error — check the field, not the status code. When the blocker is temporal (a closed gate rather than a restricted post), retryAfter tells you when to offer the slot instead.
This step earns its place on installation deliveries. A stop inside a military installation can only be served by a driver holding a current credential for that specific installation, and credentialedDriversAvailable tells you whether one is available before the customer commits. The alternative — which is what happens on platforms that do not model this — is the customer placing an order, waiting, and having it cancelled because the assigned courier turns out to have no base access, repeatedly.
Step A — Quote (optional)
If capabilities.quotes is true, request pricing before the customer commits:
bash
curl -X POST "https://api.sandbox.getonpoint.io/delivery/v1/quotes" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d @examples/create-quote.jsonTwo non-success cases to handle:
- Quoting not enabled for your account —
403with codeQUOTES_NOT_SUPPORTED. Checkcapabilities.quotesfirst and skip this step entirely. - No carrier can serve the delivery —
200with an emptyquotesarray and anoQuoteReason(OUTSIDE_COVERAGE,NO_CAPACITY,OUTSIDE_HOURS,REQUIREMENT_UNSUPPORTED). This is a normal answer, not an error; surface it as "delivery unavailable" in checkout.
Step B — Create delivery
bash
curl -X POST "https://api.sandbox.getonpoint.io/delivery/v1/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8f2b1c94-1f6a-4c3e-9f10-3d5b7a2e6c11" \
-d @examples/create-delivery-asap.jsonIdempotency-Key is required. Generate a fresh UUIDv4 per logical create — not per HTTP attempt — and reuse it on every retry of that create. Retrying with the same key and body replays the original response (200 with Idempotency-Replayed: true) instead of creating a second delivery. See Idempotency for the full state table.
Initial status: Inspect status in the response:
| Status | Meaning |
|---|---|
accepted | Delivery created; dispatch in progress |
dispatch_failed | Created but no driver assigned yet — may resolve or become terminal |
scheduled | Scheduled for a future window |
Step C — Register webhooks
Configure an HTTPS endpoint in the OnPoint dashboard (or via your account manager). See Webhooks for signature verification and event catalog.
Step D — Poll or subscribe
Prefer webhooks for status updates. For reconciliation and recovery use GET /deliveries/{deliveryId}, GET /deliveries?clientOrderReference={ref} to find an order by your own ID, or GET /deliveries?from={lastSeenTimestamp} to sweep everything that changed while you were down.
If the gap was on the webhook side — your endpoint was down, misconfigured, or behind a bad deploy — use GET /events?delivered=false&since= instead. It returns exactly the events that never reached you, oldest first, with payloads identical to the originals, so you can replay them through the same handler rather than reconstructing state.
Step E — Test the whole thing
In sandbox, drive a delivery through its lifecycle yourself:
bash
curl -X POST ".../deliveries/del_.../simulate" \
-H "Authorization: Bearer YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-d '{"advanceTo":"delivered"}'Every intervening transition fires a real webhook, signed with your real signing secret — so your signature verification, the most error-prone part of the integration, is actually exercised. Transitions are validated against the production state machine, so an illegal jump fails here exactly as it would in production.
Test the unhappy paths too. {"advanceTo":"failed","statusReasonCode":"denied_at_gate"} exercises the branch that is hardest to reproduce for real and most likely to be wrong.
5. Units and formats
| Field type | Format |
|---|---|
| Money | Integer cents (USD default) |
| Weight | Grams |
| Dimensions | Centimetres |
| Phone | E.164 (+14155551212) |
| Timestamps | UTC ISO-8601 (2026-07-26T21:15:00Z) |
6. Store pickups
For recurring pickup locations, register a store reference with OnPoint during onboarding. Then supply only pickup.storeReference instead of a full address:
json
{
"pickup": {
"storeReference": "STORE-SF-001"
},
"dropoff": {
"address": "401 San Antonio Rd, Mountain View, CA 94040",
"contactName": "Jane Customer",
"contactPhone": "+14155559876"
}
}7. Directed dispatch (partner accounts only)
Skip this section unless capabilities.ownedFleetDispatch is true. For a standard merchant account it is false, GET /teams returns an empty array, and you should omit the dispatch block entirely — OnPoint recruits drivers, assigns them by market, and routes your delivery. That is the product; there is nothing to configure.
Directed dispatch exists for platform partners and the OnPoint dashboard, which run their own dispatch against the OnPoint fleet. Those accounts may pin a delivery to a team:
json
{
"dispatch": {
"mode": "team_queue",
"teamReference": "TEAM-EAST-BAY"
}
}Sending team_queue or auto_assign without the capability returns 422 UNSUPPORTED_REQUIREMENT rather than quietly falling back to marketplace, so a partner can never believe a delivery was pinned when it was not.
Partner accounts can also map pickup.storeReference to a team during onboarding and omit the block per request.
8. Specifying verification
Verification is declared per stop and per mechanism, each with a level of required, preferred, or none:
json
{
"verification": {
"dropoff": {
"signature": { "level": "required", "collectSignerName": true },
"identity": { "level": "required", "minimumAge": 21 },
"photo": { "level": "preferred", "substituteWhenProhibited": "signature" }
}
}
}preferred is the level worth understanding. required means the stop fails if the check cannot be satisfied; preferred means it is attempted and the delivery still completes if it cannot be. Without that middle level you would be choosing between failing a delivery over a missing signature and never asking for one.
Two things happen to your request before it is enforced:
- Cargo attributes add requirements. Declaring
package.contains: ["alcohol"]raises an identity check withminimumAge: 21whether or not you asked for one. You do not have to encode compliance rules in your checkout, and you cannot accidentally omit them. - Destination policy can remove them. Where photography is prohibited — true across much of a military installation — photo capture is suppressed and
substituteWhenProhibitedis enforced instead.
Read resolvedVerification on the delivery to see what will actually be enforced, including a derived[] list of what was added and a suppressed[] list of what was refused and why. Never assume your request was applied verbatim; a suppressed photo POD reported here is very different from a carrier that simply forgot.
9. Delivering onto a military installation
A street address is not enough inside an installation. Use the structured blocks rather than packing detail into instructions, which is capped at 280 characters and is not dispatchable:
json
{
"dropoff": {
"address": "2175 Reilly Rd, Fort Liberty, NC 28310",
"installation": { "code": "INST-FTLIBERTY" },
"unit": {
"buildingNumber": "C-3421",
"room": "214B",
"deliveryPoint": "cq_desk"
}
},
"handling": {
"handoff": "cq_desk",
"undeliverableAction": "fallback_handoff",
"fallbackHandoff": "visitor_center"
}
}What installation.code buys you is dispatch filtering: the order is only offered to drivers holding a current credential scoped to that installation. Omit it and the stop is treated as an ordinary commercial address, which is how orders end up assigned to drivers who cannot get through the gate.
Four things to know:
cq_deskis the Charge of Quarters desk — the staffed 24-hour desk in a barracks, and the only lawful handoff point in most of them, since drivers cannot reach individual rooms.- Set a
fallbackHandoff. Gate denial is routine, not exceptional. A driver turned away with no pre-agreed fallback has to improvise, and that is how packages get abandoned or returned at full cost. - Access policy changes fast. Force protection posture can tighten with under a day's notice. Subscribe to
installation.access_policy_changedrather than caching serviceability across a scheduled window. - APO/FPO/DPO addresses are USPS-only. No commercial carrier can serve them. OnPoint rejects them at quote time with
422UNSUPPORTED_DESTINATIONrather than accepting the order and failing later.
Enumerate coverage with GET /installations, and read one installation's gates, hours, permitted proof types, and prohibited cargo with GET /installations/{code}.
10. Build a tolerant client
OnPoint fulfils deliveries through its own fleet in some markets and partner carrier networks in others. The contract is identical either way — no carrier-specific fields are ever exposed — so never branch on which carrier serves a market.
The major version in the base path (/delivery/v1) changes only for breaking changes. These ship without notice and your client must absorb them:
- New optional request fields
- New properties on response objects
- New values in
status,eventType, and errorcodeenums - New webhook event types and endpoints
Two rules follow: ignore unknown properties instead of rejecting them, and handle unknown enum values with a default branch instead of throwing. A client that crashes on an unrecognized delivery status breaks the first time we add one.
Breaking changes ship as /delivery/v2 with at least 12 months of parallel v1 operation.
Next steps
- OpenAPI specification — full request/response schemas
- Webhooks — event catalog and verification
- Delivery lifecycle — status definitions
- Examples — copy-paste JSON payloads