Idempotency-Key is required. Send a value you generate per order, and
retry the same call with the same key if you never saw a response: a
replay within 24 hours returns the original response, status code
included, without dispatching a second courier or debiting a second
time. The same key with a different body is a 409, and so is a
repeat sent while the original request is still being processed
(idempotency_conflict both times). That repeat created nothing and
does not mean the original failed: once the original has answered,
the same request with the same key replays its answer.
dropoff.phone is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with validation_failed.
external_id is your own order reference. It is unique per key world,
echoed in every payload and webhook, and filterable on the list, so you
can reconcile without ever storing our ids.
pickup_ready_at schedules the pickup for a future moment, between 10
minutes and 29 days out. Absent, the order dispatches now.
How the customer pays is payment_method. cash has the courier
collect at the door, up to a cap. card mints a branded payment link
in payment.url, which Listo sends your customer itself by WhatsApp
(and by SMS when the WhatsApp does not reach them), so you do not need
to send it; the order holds at awaiting_payment until the customer
pays. prepaid means you have
already been paid and the order dispatches immediately.
On a prepaid create that binds no quote, business_fee_share is
forced to 100 and business_fee_cap_cents is ignored, whatever you
send: you have already collected from your customer, so the whole fee
is yours. A create that binds a quote is held to the split the quote
was priced with, so request the quote with payment_method: prepaid
or with no payment_method: either is always priced at share 100 and
binds a prepaid create. Any other quote is refused with 422 validation_failed, and the first thing that does not match is the one
named. A quote requested with another payment_method (cash or
card) is refused naming payment_method. A quote whose split is not
the one a prepaid order has (share 100, no ceiling) is refused naming
business_fee_share or business_fee_cap_cents, and error.message
says why and what to send instead: a new quote requested with
payment_method: prepaid.
vehicle is moto (the default) or car, and manifest.items[].size
is small (the default), medium, large or xlarge. A trip goes by
car when you ask for one or when any item is sized xlarge, and for no
other reason. A car is a sedan, never a van, can take longer to assign,
and adds a surcharge inside fee_cents. The delivery’s vehicle says
what the trip was priced and dispatched as; courier.vehicle_type is
what actually arrived, which can be a car on a moto trip without the
fee changing.
A create that binds a quote is held to the vehicle the quote was priced
as. Leave vehicle out and it inherits the quote’s. Say moto against
a car quote, car against a moto quote, or send an item sized xlarge
against a moto quote, and it is refused with 422 validation_failed
naming vehicle (or manifest.items when the sizes are what
disagree). Re-quote with the cart you mean to send.
simulate rehearses what goes wrong, and it works on a test key only.
A live key that sends it is refused with 422 validation_failed
naming simulate, and so is a value that is not one of the three
below. Leave it out and the delivery plays the normal happy path.
returned: the courier reaches the door, cannot hand the order
over and brings it back. The delivery goes to returning and ends
at returned with failure_reason: customer_unavailable, and the
return is billed as a live one is: one return_fee entry in
charges covers both legs (see charges).
no_courier: the create answers the same 409 no_couriers_available a live create answers when every courier is
busy, and nothing is created, charged or recorded under your
Idempotency-Key. There is no delivery to retry; repeat the call.
delayed_pickup: the courier is late. Pickup happens about 25
minutes after the create and the drop-off about 5 minutes after
that, counted from pickup_ready_at instead when you sent one (the
delivery waits in scheduled until then). It goes through the
same statuses as the normal run.
In test mode a delivery may go from dispatching straight to
picking_up without ever showing a courier_assigned status, in
every scenario. Do not wait for it: courier_assigned_at and the
courier object are filled in.
There is no scenario for a courier who gives up before pickup. The
same Idempotency-Key with a different simulate is a 409 idempotency_conflict, like any other changed body, once a request has
used the key (it created a delivery, or it is still being processed).
A request refused before it created anything, such as the no_courier
409, leaves the key free: send it again with a different simulate,
or none, and it is a new request. A returned delivery cannot be
retried (409 not_retriable): its order ended.
A cart too large for one courier trip is refused with 422 order_too_large, naming manifest.items, and nothing is dispatched or
debited. Split it across two deliveries.
Listo delivers in Mexico. A store that turns out to be somewhere else is
refused with 422 unsupported_country and nothing is dispatched or
debited; it is final rather than retryable, and the same store will be
refused every time.
A store whose pickup point we cannot currently vouch for is refused
with 409 pickup_point_pending, and nothing is dispatched or debited:
until it is confirmed a courier could be sent to the wrong place.
Usually the store’s address has just changed and the confirmation has
not landed yet, but not always, so do not read it as “the merchant
moved”. Unlike the refusal above it this one IS retryable, because
nothing about your request is wrong and the identical call succeeds once
the pickup point is confirmed.
Retry it on a minutes-scale backoff, for a bounded number of attempts,
and then put it in front of a human rather than keep polling. Most moves
land within minutes on their own; a few are held up by something only
our staff can clear and take considerably longer, and this response does
not tell the two apart. There is no Retry-After on it for that reason:
we would be inventing the number. The restaurant can also unblock it
themselves from their own Listo console, which is usually the faster of
the two.
When every courier near the trip is busy as the create prices it, the
create is refused with 409 no_couriers_available, and nothing is
dispatched or debited. It is retryable: repeat the identical request,
with the same Idempotency-Key, after a few minutes, for a bounded
number of attempts. Nothing was recorded under that key, so the repeat
creates the order once a courier is free rather than replaying the
refusal.
A live store whose Plan Listo is not paid up is refused with 402 plan_required, and nothing is dispatched or debited. It is a different
refusal from 402 insufficient_balance, which is about your balance:
read error.code, never the status alone. error.message names the
reason (no active plan, a charge that is pending, a plan that ended on a
given date, a plan whose included deliveries for the cycle are all used,
or a store that cannot request couriers right now), and it
is not retryable. See
Plan and billing.
A dropoff beyond the store’s 15 km service area is refused with 422 out_of_service_area, which is final. The courier network can also
refuse the distance of a dropoff at the very edge of that circle, and
the answer is the same 422, whether it refuses on the price or on the
create itself. On the create it comes after the delivery was recorded,
so the delivery stays, ended at no_courier with failure_reason: delivery_failed under your external_id, its fee debit returned in
full and nothing else charged; replaying the request answers the same
422. Send that order to another address under a new external_id.
Your API key, sent as Authorization: Bearer <key>. Live and test keys are separate; the key you send decides which world the call acts in. To try calls, use a test key (it starts with lst_test_): no real courier is sent and no real money moves, but webhooks fire and the dropoff.phone you give can receive WhatsApp messages, so use your own number. A live key (it starts with lst_live_) books and charges real deliveries.
Required: a call without one is refused with validation_failed, naming this header. Generate a NEW key for each logical call (a UUID is a good one), at most 255 characters, and send the SAME key when you retry that call after a timeout or a dropped connection. The same key with the same body returns the ORIGINAL response, status code included, instead of doing the work again, for 24 hours. The same key with a different body is refused with 409 idempotency_conflict, and so is a retry that arrives while the first request is still being processed (try again shortly). A key belongs to your account and to the mode of the API key that sent it (live and test keys never meet), so the same text from two accounts, or from a live and a test key, is two different keys.
The descriptive item list for the courier. Money never comes from it;
its only effect on the trip is that an xlarge item makes it a car.
Absent, null, {} and {"items": null} all mean no cart.
Test keys only: the scenario the simulated courier plays. A live
key that sends it is refused.
Available options:
returned,
no_courier,
delayed_pickup
Response
Created.
The shape of a delivery: every field the object can carry, with the
nullability each one really has. It is the response of every operation
that answers a delivery (the create, the fetch, a list row, the
reschedule, the cancel, the retry and the refund) and the data of
every full delivery event, so ONE type holds all of them.
Every operation returns the whole object. A field that a given call
cannot yet have is present and null rather than missing: a create
response has a null courier, because no courier has been assigned
yet, and a reschedule only ever answers with a still-scheduled order,
so its delivered_at is null.
Money is integer centavos, timestamps are ISO-8601 in UTC, and every
enum below is ours rather than a passthrough of anyone else's.
Which lane created the delivery. api is one you created through
POST /v1/deliveries. console is one the store dispatched itself
from the Listo merchant console, which a store can do only at the
full access tier. whatsapp is one a customer ordered from the
store in a chat, which the store then accepted; no operator typed
it anywhere.
Always present, never null, and fixed for the life of the delivery.
A console order carries a null external_id, but read this field
rather than that null: the missing reference is a consequence of the
lane, not a way of identifying it.
delivered, returned and canceled are terminal. no_courier is
a dead end you can retry or cancel. A returned delivery is this
same object throughout; there is no second delivery for the return
leg.
returning: the drop-off failed and the courier is bringing the
order back to the store; it ends in returned. It is NOT terminal,
and it is where a failed delivery now spends the minutes between the
courier giving up at the customer's door and the order actually
arriving back. returned means the order is back. Treat returning
as a live delivery: the courier is still moving and
delivery.courier_location events keep arriving for it.
The return charge is posted when the delivery enters returning,
not when it reaches returned, so charges is already complete on
the returning event.
awaiting_location: an order the store took through Listo's console
or its WhatsApp assistant, held for the drop-off location. No
courier is requested yet. The customer confirms the location on a
map link or by sharing it in the WhatsApp chat, or the store
confirms it from its console. The delivery then moves to
scheduled or dispatching; to no_courier when a courier
cannot be requested for it (the location is outside where the
store delivers, or the store itself cannot request a courier right
now); or to canceled if the order is canceled or the store
changes it to a pickup at the counter. A delivery you create
through this API never sits in it.
The courier tip you sent on this delivery, paid to the courier and
passed on at cost with no margin taken. Deliberately OUTSIDE
fee_cents, which is the delivery fee business_fee_cents and
customer_fee_cents sum back to. 0 when you sent none, and never
null.
Where the courier collects, snapshotted at create, so a historical
order keeps reporting where the courier actually went even after the
store is renamed or moved.
The descriptive item list you sent, for the courier. Money never
comes from it, with one exception: an item sized xlarge makes the
trip a car. Null when you sent none.
What the trip was priced and dispatched as: car when you asked for
one or when an item was sized xlarge, moto otherwise. A car's
surcharge is inside fee_cents. Always present, never null, and
fixed for the life of the delivery.
Not the vehicle that arrived. That is courier.vehicle_type, which
can be a car on a moto trip without the fee changing.
When the courier is expected at the customer. Null until there is
an estimate, and null again once the drop-off has failed
(returning, returned): the courier is then bringing the order
back to the store, there is no arrival at the customer left to
describe, and the time it gets back to the store is never published
here.
The branded live tracking page for the customer: the map, the
courier, the delivery code and what is owed at the door. Null when
there is no page to point at, so you never relay a dead link.
There is a page from the moment the order is dispatched, including
the whole window before a courier is assigned, and for 48 hours
after the order is delivered, canceled or returned. It is null while
an order is still held awaiting payment or awaiting a confirmed
drop-off pin, and null again once that 48 hours has passed.
Present only when the order carries a dropoff PIN: when you asked
for one (verification.dropoff_pincode on the create), or when
Listo requires a PIN on every delivery of this store's account, in
which case it is present even though you did not ask. pincode is
null until the order is actually dispatched, which is when the code
is minted.
The return code (código de devolución). When an order cannot be
delivered and the courier brings it back to the store, the courier
must be given this code at the store to hand the order over; without
it the return cannot be completed. pincode is a 4-digit string
while the delivery is returning, and null in every other state
(before the return, and once it is returned). The object is
always present, so this field never needs a presence check. It
arrives in delivery.status_changed (the move into returning) and
in delivery.updated, which carry the whole object. A new attempt
(a retry) gets its own code. Show it to the store, and never to the
customer.
Proof of delivery. Null unless we hold some, and each field is null
when that piece is missing; a photo with no signature is normal. The
URLs are Listo's own signed links to the stored copy and are valid
for 24 hours from the response (or webhook) that carried them:
fetch GET /v1/deliveries/{id} again for a fresh link, or keep a
copy. A URL is null until Listo has its own copy of the image
(a few seconds after the courier takes it); the copy's arrival is
announced by a delivery.updated webhook that carries the link.
It may come from a delivery attempt that failed at the
door, so it is not by itself proof that the order was handed over:
proofs.dropoff.delivered says which. Prefer proofs, which keeps
each moment of the trip apart.
The evidence the trip left, one entry per moment. Each moment is
null until it happens (nothing has been captured for it yet), so
this object is always present with its three keys. A photo or
signature link is Listo's own signed link to the stored copy, and
it is null until Listo holds that copy (a few seconds after the
courier takes the photo): the rest of the moment, the signer, the
time and the distance, is there from the start, and when the copy
lands a delivery.updated webhook carries the link. Each link is
valid for 24 hours from the response (or webhook) that carried
it: it is a short-lived signed link, never a permanent one. A
partner that needs the image later fetches
GET /v1/deliveries/{id} again for a fresh link, or keeps a copy.
Why a dispatch failed, bucketed by whose side the problem is on, so
you can act on it rather than just retry blindly.
no_couriers_available is the retriable one.
delivery_failed is the honest catch-all. Null unless a failure was
recorded.
What a trip that was driven but wasted was billed at: the delivery
fee for each leg driven, less your own store markup. Empty on
almost every delivery, and never null.
This amount changed on 2026-08-29. It used to be what the
wasted trip cost Listo, so expect larger values on the same orders
from that date onward. The field itself is unchanged.
Always present on a delivery object, so treat an absent charges
as impossible rather than as "no charges". It is in this schema's
required list for that reason.
A charge can arrive on the delivery's final
delivery.status_changed or, when the cost settles afterwards, on
a later delivery.updated. You cannot assume which, so read this
on both: billing the moment a delivery reaches its terminal status
bills short on every order whose cost landed late.
The same movement appears on GET /v1/ledger as a signed row, so
it reads negative there while amount_cents here is positive.
Every time money went back to the customer on this order, newest
first. Empty on an order that has never been refunded, and never
null.
Always present on a delivery object, so treat an absent refunds
as impossible rather than as "no refunds". It is in this schema's
required list for that reason, exactly as charges above is.
It is a LIST because refunds repeat: a partial refund is followed
by another refund, each with its own amount, its own reason and its
own outcome. Every refund we can see is here whoever made it, which
is what makes the list worth reconciling against. The connected
account belongs to the merchant of record, which can refund a charge
on it without going through us at all; those arrive as
requested_via: external, and a list that showed only the refunds
you asked for would silently disagree with this order's own
payment.status.
A refund rides delivery.updated, never an event of its own.
The sum of the SUCCEEDED refunds above, in Mexican peso cents. 0,
never null, on an order nothing has gone back on. A pending refund
is not counted, because the money has not moved and may yet be
refused.
Published rather than left for you to compute, because the array
cannot always be summed correctly: a refund of the remaining balance
carries no amount_cents until it is confirmed, so adding the
list up counts it as zero and reads the whole charge as still
refundable. We take that case pessimistically instead, as having
taken the entire remaining balance, because guessing low ends in
refunding a customer twice.
What a further refund could still return: what the customer paid
(order_value_cents plus customer_fee_cents, which IS the charge)
minus refunded_cents, floored at zero. 0, never null.
0 on any order with no collected card payment behind it, whatever
its value. Cash never touched our rails, a prepaid order's customer
is yours and we hold nothing of theirs, and an order still awaiting
payment was never charged, so there is nothing on any of them a
refund could return.
This is the ceiling POST /v1/deliveries/{id}/refunds refuses an
amount_cents against, so read it before you call rather than
discovering the bound from a refund_amount_too_large.
When a courier was first observed on the current attempt. Honestly
an observation rather than an assignment clock, because no
assignment time is published to us. Null on an attempt that never
had one.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Listo Partner API","description":"Quote, create and track deliveries, and manage your balance, through the Listo Partner Delivery API.","colors":{"primary":"#15171C","light":"#F4F4F5","dark":"#15171C"},"logo":{"light":"https://mintcdn.com/listo-c5a11a00/mreCwXzcEA2bSZog/logo/light.svg?fit=max\u0026auto=format\u0026n=mreCwXzcEA2bSZog\u0026q=85\u0026s=da2b28d464dc6987329dfd5971085dd7","dark":"https://mintcdn.com/listo-c5a11a00/mreCwXzcEA2bSZog/logo/dark.svg?fit=max\u0026auto=format\u0026n=mreCwXzcEA2bSZog\u0026q=85\u0026s=ad013a6a0d979eb46c1cea550ec6a460","href":"/"},"favicon":"https://mintcdn.com/listo-c5a11a00/mreCwXzcEA2bSZog/favicon.svg?fit=max\u0026auto=format\u0026n=mreCwXzcEA2bSZog\u0026q=85\u0026s=f790496dfe0c2ad9971d8de95e45be8d","api":{"playground":{"display":"interactive"}},"thumbnails":{"preset":"minimal","appearance":"dark","background":"https://mintcdn.com/listo-c5a11a00/lF-Gm03tnDMquh_z/images/og-background.jpg?fit=max\u0026auto=format\u0026n=lF-Gm03tnDMquh_z\u0026q=85\u0026s=d73469201c8ffe28afc598d23fde5d84","fonts":{"family":"Archivo ExtraBold","source":"fonts/archivo-800.ttf","format":"ttf"},"eyebrow":{"source":"none"}},"markdown":{"instructions":["Notes on the Listo Partner Delivery API:","- Every endpoint is under `https://api.listo.delivery/v1/` and authenticates with a bearer API key.","- This documentation site is at https://docs.listo.delivery. The full machine-readable reference is the OpenAPI document at https://api.listo.delivery/docs/openapi.yaml (served by the API host, not by this site).","- Any page of this site is also available as raw markdown by appending `.md` to its URL, for example https://docs.listo.delivery/en/guides/webhooks.md. The index of every page is https://docs.listo.delivery/llms.txt and all of them in one file is https://docs.listo.delivery/llms-full.txt.","- Every page is published in English under https://docs.listo.delivery/en/ and in Spanish under https://docs.listo.delivery/es/, with the same path after the language. The API reference is under https://docs.listo.delivery/api-reference/ in English and https://docs.listo.delivery/es/api-reference/ in Spanish.","- To debug an integration yourself: `GET /v1/request_logs` lists the calls your keys made with what you sent and what was answered, `GET /v1/events` lists the events Listo emitted, `GET /v1/webhook_deliveries` lists every attempt to deliver them, `POST /v1/events/{id}/resend` sends one event again and `POST /v1/test_webhooks` sends a sample event (`\"test\": true`). The page https://docs.listo.delivery/en/guides/debugging describes them. A resent event is a snapshot from when it happened; `GET /v1/deliveries` is still how you recover current state.","- Every error response has `error.doc_url`, a page that explains that error code and what to do, and `error.request_id`, the same value as the `X-Request-Id` response header. All the pages are listed at https://docs.listo.delivery/en/errors. Filter `GET /v1/request_logs` with `request_id` to read the logged call.","- A `409 account_exists` from `POST /v1/stores` means the restaurant already has its own Listo account: connect it instead of creating it, as the page https://docs.listo.delivery/en/guides/connect-existing-account describes.","- Every live store carries one Plan Listo, charged to the partner. The exception is a connected restaurant that had already paid its current month: it keeps paying its own plan until its next renewal, shown as `paid_by: \"store\"` with the handover date in `pending_paid_by_on`. A `402 plan_required` from a quote, a delivery create or a retry means the store's plan is not paid or has ended; the page https://docs.listo.delivery/en/guides/plan-and-billing describes the plan, the `plan` block on a store and how to end or restart it.","- An AI assistant (Claude Code, Cursor, Codex) can act through the same API key at `https://api.listo.delivery/mcp`, an MCP server over Streamable HTTP authenticated with `Authorization: Bearer \u003cAPI key>`. Every tool runs the matching `/v1/` endpoint, and each one that moves money first previews and then needs a confirmation token; the page https://docs.listo.delivery/en/guides/connect-ai-client lists the tools and describes the two steps."]},"styling":{"latex":true},"navigation":{"languages":[{"language":"en","default":true,"navbar":{"links":[{"href":"https://status.listo.delivery","label":"Status"}]},"href":"en/quickstart"},{"language":"es","navbar":{"links":[{"href":"https://status.listo.delivery","label":"Estado del servicio"}]},"href":"es/quickstart"}]}},"deployment":{"deploymentMetadataValue":{"subdomain":"listo-c5a11a00","hasAudioNarration":false,"actualSubdomain":"listo-c5a11a00","customDomains":["docs.listo.delivery"],"gitSource":{"type":"github","owner":"davidalejandroaguilar","repo":"listo","deployBranch":"main","contentDirectory":"docs-site","isPrivate":true,"mountPath":"","label":"","_id":"6aed6954347cccfbc77ed1c5"},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6aeeb167ae7fce64eae954de","clientVersion":"0.0.4076","searchFilterCounts":[{"language":"en","tag":"Errors","count":43},{"language":"en","tag":"Reference","count":43},{"language":"es","tag":"Errores","count":43},{"language":"es","tag":"Referencia","count":43},{"language":"en","tag":"Guides","count":9},{"language":"es","tag":"Guías","count":9},{"language":"en","tag":"Stores","count":8},{"language":"es","tag":"Tiendas","count":8},{"language":"en","tag":"Deliveries","count":7},{"language":"es","tag":"Envíos","count":7},{"language":"en","tag":"Webhooks","count":6},{"language":"es","tag":"Webhooks","count":6},{"language":"en","tag":"Concepts","count":5},{"language":"es","tag":"Conceptos","count":5},{"language":"en","tag":"Card onboarding","count":4},{"language":"en","tag":"Events","count":4},{"language":"es","tag":"Alta de cobros con tarjeta","count":4},{"language":"es","tag":"Eventos","count":4},{"language":"en","tag":"Withdrawals","count":3},{"language":"es","tag":"Retiros","count":3}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":":is(#sidebar-content,#mobile-nav) li[data-active=\"true\"]>a{background-color:#ffc400;color:#15171c}"}],"page":{"pagePath":"api-reference/deliveries/create-a-delivery","slug":"/api-reference/deliveries/create-a-delivery","pageMetadata":{"title":"Create a delivery","description":"Dispatches a delivery.\n\n`Idempotency-Key` is required. Send a value you generate per order, and\nretry the same call with the same key if you never saw a response: a\nreplay within 24 hours returns the original response, status code\nincluded, without dispatching a second courier or debiting a second\ntime. The same key with a different body is a `409`, and so is a\nrepeat sent while the original request is still being processed\n(`idempotency_conflict` both times). That repeat created nothing and\ndoes not mean the original failed: once the original has answered,\nthe same request with the same key replays its answer.\n\n`dropoff.phone` is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with `validation_failed`.\n\n`external_id` is your own order reference. It is unique per key world,\nechoed in every payload and webhook, and filterable on the list, so you\ncan reconcile without ever storing our ids.\n\n`pickup_ready_at` schedules the pickup for a future moment, between 10\nminutes and 29 days out. Absent, the order dispatches now.\n\nHow the customer pays is `payment_method`. `cash` has the courier\ncollect at the door, up to a cap. `card` mints a branded payment link\nin `payment.url`, which Listo sends your customer itself by WhatsApp\n(and by SMS when the WhatsApp does not reach them), so you do not need\nto send it; the order holds at `awaiting_payment` until the customer\npays. `prepaid` means you have\nalready been paid and the order dispatches immediately.\n\nOn a `prepaid` create that binds no quote, `business_fee_share` is\nforced to 100 and `business_fee_cap_cents` is ignored, whatever you\nsend: you have already collected from your customer, so the whole fee\nis yours. A create that binds a quote is held to the split the quote\nwas priced with, so request the quote with `payment_method: prepaid`\nor with no `payment_method`: either is always priced at share 100 and\nbinds a `prepaid` create. Any other quote is refused with `422\nvalidation_failed`, and the first thing that does not match is the one\nnamed. A quote requested with another `payment_method` (`cash` or\n`card`) is refused naming `payment_method`. A quote whose split is not\nthe one a `prepaid` order has (share 100, no ceiling) is refused naming\n`business_fee_share` or `business_fee_cap_cents`, and `error.message`\nsays why and what to send instead: a new quote requested with\n`payment_method: prepaid`.\n\n`vehicle` is `moto` (the default) or `car`, and `manifest.items[].size`\nis `small` (the default), `medium`, `large` or `xlarge`. A trip goes by\ncar when you ask for one or when any item is sized `xlarge`, and for no\nother reason. A car is a sedan, never a van, can take longer to assign,\nand adds a surcharge inside `fee_cents`. The delivery's `vehicle` says\nwhat the trip was priced and dispatched as; `courier.vehicle_type` is\nwhat actually arrived, which can be a car on a `moto` trip without the\nfee changing.\n\nA create that binds a quote is held to the vehicle the quote was priced\nas. Leave `vehicle` out and it inherits the quote's. Say `moto` against\na car quote, `car` against a moto quote, or send an item sized `xlarge`\nagainst a moto quote, and it is refused with `422 validation_failed`\nnaming `vehicle` (or `manifest.items` when the sizes are what\ndisagree). Re-quote with the cart you mean to send.\n\n`simulate` rehearses what goes wrong, and it works on a test key only.\nA live key that sends it is refused with `422 validation_failed`\nnaming `simulate`, and so is a value that is not one of the three\nbelow. Leave it out and the delivery plays the normal happy path.\n\n- `returned`: the courier reaches the door, cannot hand the order\n over and brings it back. The delivery goes to `returning` and ends\n at `returned` with `failure_reason: customer_unavailable`, and the\n return is billed as a live one is: one `return_fee` entry in\n `charges` covers both legs (see `charges`).\n- `no_courier`: the create answers the same `409\n no_couriers_available` a live create answers when every courier is\n busy, and nothing is created, charged or recorded under your\n `Idempotency-Key`. There is no delivery to retry; repeat the call.\n- `delayed_pickup`: the courier is late. Pickup happens about 25\n minutes after the create and the drop-off about 5 minutes after\n that, counted from `pickup_ready_at` instead when you sent one (the\n delivery waits in `scheduled` until then). It goes through the\n same statuses as the normal run.\n\nIn test mode a delivery may go from `dispatching` straight to\n`picking_up` without ever showing a `courier_assigned` status, in\nevery scenario. Do not wait for it: `courier_assigned_at` and the\ncourier object are filled in.\n\nThere is no scenario for a courier who gives up before pickup. The\nsame `Idempotency-Key` with a different `simulate` is a `409\nidempotency_conflict`, like any other changed body, once a request has\nused the key (it created a delivery, or it is still being processed).\nA request refused before it created anything, such as the `no_courier`\n409, leaves the key free: send it again with a different `simulate`,\nor none, and it is a new request. A `returned` delivery cannot be\nretried (`409 not_retriable`): its order ended.\n\nA cart too large for one courier trip is refused with `422\norder_too_large`, naming `manifest.items`, and nothing is dispatched or\ndebited. Split it across two deliveries.\n\nListo delivers in Mexico. A store that turns out to be somewhere else is\nrefused with `422 unsupported_country` and nothing is dispatched or\ndebited; it is final rather than retryable, and the same store will be\nrefused every time.\n\nA store whose pickup point we cannot currently vouch for is refused\nwith `409 pickup_point_pending`, and nothing is dispatched or debited:\nuntil it is confirmed a courier could be sent to the wrong place.\nUsually the store's address has just changed and the confirmation has\nnot landed yet, but not always, so do not read it as \"the merchant\nmoved\". Unlike the refusal above it this one IS retryable, because\nnothing about your request is wrong and the identical call succeeds once\nthe pickup point is confirmed.\n\nRetry it on a minutes-scale backoff, for a bounded number of attempts,\nand then put it in front of a human rather than keep polling. Most moves\nland within minutes on their own; a few are held up by something only\nour staff can clear and take considerably longer, and this response does\nnot tell the two apart. There is no `Retry-After` on it for that reason:\nwe would be inventing the number. The restaurant can also unblock it\nthemselves from their own Listo console, which is usually the faster of\nthe two.\n\nWhen every courier near the trip is busy as the create prices it, the\ncreate is refused with `409 no_couriers_available`, and nothing is\ndispatched or debited. It is retryable: repeat the identical request,\nwith the same `Idempotency-Key`, after a few minutes, for a bounded\nnumber of attempts. Nothing was recorded under that key, so the repeat\ncreates the order once a courier is free rather than replaying the\nrefusal.\n\nA live store whose Plan Listo is not paid up is refused with `402\nplan_required`, and nothing is dispatched or debited. It is a different\nrefusal from `402 insufficient_balance`, which is about your balance:\nread `error.code`, never the status alone. `error.message` names the\nreason (no active plan, a charge that is pending, a plan that ended on a\ngiven date, a plan whose included deliveries for the cycle are all used,\nor a store that cannot request couriers right now), and it\nis not `retryable`. See\n[Plan and billing](https://docs.listo.delivery/en/guides/plan-and-billing).\n\nA dropoff beyond the store's 15 km service area is refused with `422\nout_of_service_area`, which is final. The courier network can also\nrefuse the distance of a dropoff at the very edge of that circle, and\nthe answer is the same `422`, whether it refuses on the price or on the\ncreate itself. On the create it comes after the delivery was recorded,\nso the delivery stays, ended at `no_courier` with `failure_reason:\ndelivery_failed` under your `external_id`, its fee debit returned in\nfull and nothing else charged; replaying the request answers the same\n`422`. Send that order to another address under a new `external_id`.\n","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/deliveries","href":"/api-reference/deliveries/create-a-delivery","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n a: \"a\",\n code: \"code\",\n li: \"li\",\n p: \"p\",\n ul: \"ul\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsx(_components.p, {\n children: \"Dispatches a delivery.\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"Idempotency-Key\"\n }), \" is required. Send a value you generate per order, and\\nretry the same call with the same key if you never saw a response: a\\nreplay within 24 hours returns the original response, status code\\nincluded, without dispatching a second courier or debiting a second\\ntime. The same key with a different body is a \", _jsx(_components.code, {\n children: \"409\"\n }), \", and so is a\\nrepeat sent while the original request is still being processed\\n(\", _jsx(_components.code, {\n children: \"idempotency_conflict\"\n }), \" both times). That repeat created nothing and\\ndoes not mean the original failed: once the original has answered,\\nthe same request with the same key replays its answer.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"dropoff.phone\"\n }), \" is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with \", _jsx(_components.code, {\n children: \"validation_failed\"\n }), \".\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"external_id\"\n }), \" is your own order reference. It is unique per key world,\\nechoed in every payload and webhook, and filterable on the list, so you\\ncan reconcile without ever storing our ids.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"pickup_ready_at\"\n }), \" schedules the pickup for a future moment, between 10\\nminutes and 29 days out. Absent, the order dispatches now.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"How the customer pays is \", _jsx(_components.code, {\n children: \"payment_method\"\n }), \". \", _jsx(_components.code, {\n children: \"cash\"\n }), \" has the courier\\ncollect at the door, up to a cap. \", _jsx(_components.code, {\n children: \"card\"\n }), \" mints a branded payment link\\nin \", _jsx(_components.code, {\n children: \"payment.url\"\n }), \", which Listo sends your customer itself by WhatsApp\\n(and by SMS when the WhatsApp does not reach them), so you do not need\\nto send it; the order holds at \", _jsx(_components.code, {\n children: \"awaiting_payment\"\n }), \" until the customer\\npays. \", _jsx(_components.code, {\n children: \"prepaid\"\n }), \" means you have\\nalready been paid and the order dispatches immediately.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"On a \", _jsx(_components.code, {\n children: \"prepaid\"\n }), \" create that binds no quote, \", _jsx(_components.code, {\n children: \"business_fee_share\"\n }), \" is\\nforced to 100 and \", _jsx(_components.code, {\n children: \"business_fee_cap_cents\"\n }), \" is ignored, whatever you\\nsend: you have already collected from your customer, so the whole fee\\nis yours. A create that binds a quote is held to the split the quote\\nwas priced with, so request the quote with \", _jsx(_components.code, {\n children: \"payment_method: prepaid\"\n }), \"\\nor with no \", _jsx(_components.code, {\n children: \"payment_method\"\n }), \": either is always priced at share 100 and\\nbinds a \", _jsx(_components.code, {\n children: \"prepaid\"\n }), \" create. Any other quote is refused with \", _jsx(_components.code, {\n children: \"422 validation_failed\"\n }), \", and the first thing that does not match is the one\\nnamed. A quote requested with another \", _jsx(_components.code, {\n children: \"payment_method\"\n }), \" (\", _jsx(_components.code, {\n children: \"cash\"\n }), \" or\\n\", _jsx(_components.code, {\n children: \"card\"\n }), \") is refused naming \", _jsx(_components.code, {\n children: \"payment_method\"\n }), \". A quote whose split is not\\nthe one a \", _jsx(_components.code, {\n children: \"prepaid\"\n }), \" order has (share 100, no ceiling) is refused naming\\n\", _jsx(_components.code, {\n children: \"business_fee_share\"\n }), \" or \", _jsx(_components.code, {\n children: \"business_fee_cap_cents\"\n }), \", and \", _jsx(_components.code, {\n children: \"error.message\"\n }), \"\\nsays why and what to send instead: a new quote requested with\\n\", _jsx(_components.code, {\n children: \"payment_method: prepaid\"\n }), \".\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"vehicle\"\n }), \" is \", _jsx(_components.code, {\n children: \"moto\"\n }), \" (the default) or \", _jsx(_components.code, {\n children: \"car\"\n }), \", and \", _jsx(_components.code, {\n children: \"manifest.items[].size\"\n }), \"\\nis \", _jsx(_components.code, {\n children: \"small\"\n }), \" (the default), \", _jsx(_components.code, {\n children: \"medium\"\n }), \", \", _jsx(_components.code, {\n children: \"large\"\n }), \" or \", _jsx(_components.code, {\n children: \"xlarge\"\n }), \". A trip goes by\\ncar when you ask for one or when any item is sized \", _jsx(_components.code, {\n children: \"xlarge\"\n }), \", and for no\\nother reason. A car is a sedan, never a van, can take longer to assign,\\nand adds a surcharge inside \", _jsx(_components.code, {\n children: \"fee_cents\"\n }), \". The delivery’s \", _jsx(_components.code, {\n children: \"vehicle\"\n }), \" says\\nwhat the trip was priced and dispatched as; \", _jsx(_components.code, {\n children: \"courier.vehicle_type\"\n }), \" is\\nwhat actually arrived, which can be a car on a \", _jsx(_components.code, {\n children: \"moto\"\n }), \" trip without the\\nfee changing.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A create that binds a quote is held to the vehicle the quote was priced\\nas. Leave \", _jsx(_components.code, {\n children: \"vehicle\"\n }), \" out and it inherits the quote’s. Say \", _jsx(_components.code, {\n children: \"moto\"\n }), \" against\\na car quote, \", _jsx(_components.code, {\n children: \"car\"\n }), \" against a moto quote, or send an item sized \", _jsx(_components.code, {\n children: \"xlarge\"\n }), \"\\nagainst a moto quote, and it is refused with \", _jsx(_components.code, {\n children: \"422 validation_failed\"\n }), \"\\nnaming \", _jsx(_components.code, {\n children: \"vehicle\"\n }), \" (or \", _jsx(_components.code, {\n children: \"manifest.items\"\n }), \" when the sizes are what\\ndisagree). Re-quote with the cart you mean to send.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"simulate\"\n }), \" rehearses what goes wrong, and it works on a test key only.\\nA live key that sends it is refused with \", _jsx(_components.code, {\n children: \"422 validation_failed\"\n }), \"\\nnaming \", _jsx(_components.code, {\n children: \"simulate\"\n }), \", and so is a value that is not one of the three\\nbelow. Leave it out and the delivery plays the normal happy path.\"]\n }), \"\\n\", _jsxs(_components.ul, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.code, {\n children: \"returned\"\n }), \": the courier reaches the door, cannot hand the order\\nover and brings it back. The delivery goes to \", _jsx(_components.code, {\n children: \"returning\"\n }), \" and ends\\nat \", _jsx(_components.code, {\n children: \"returned\"\n }), \" with \", _jsx(_components.code, {\n children: \"failure_reason: customer_unavailable\"\n }), \", and the\\nreturn is billed as a live one is: one \", _jsx(_components.code, {\n children: \"return_fee\"\n }), \" entry in\\n\", _jsx(_components.code, {\n children: \"charges\"\n }), \" covers both legs (see \", _jsx(_components.code, {\n children: \"charges\"\n }), \").\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.code, {\n children: \"no_courier\"\n }), \": the create answers the same \", _jsx(_components.code, {\n children: \"409 no_couriers_available\"\n }), \" a live create answers when every courier is\\nbusy, and nothing is created, charged or recorded under your\\n\", _jsx(_components.code, {\n children: \"Idempotency-Key\"\n }), \". There is no delivery to retry; repeat the call.\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.code, {\n children: \"delayed_pickup\"\n }), \": the courier is late. Pickup happens about 25\\nminutes after the create and the drop-off about 5 minutes after\\nthat, counted from \", _jsx(_components.code, {\n children: \"pickup_ready_at\"\n }), \" instead when you sent one (the\\ndelivery waits in \", _jsx(_components.code, {\n children: \"scheduled\"\n }), \" until then). It goes through the\\nsame statuses as the normal run.\"]\n }), \"\\n\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"In test mode a delivery may go from \", _jsx(_components.code, {\n children: \"dispatching\"\n }), \" straight to\\n\", _jsx(_components.code, {\n children: \"picking_up\"\n }), \" without ever showing a \", _jsx(_components.code, {\n children: \"courier_assigned\"\n }), \" status, in\\nevery scenario. Do not wait for it: \", _jsx(_components.code, {\n children: \"courier_assigned_at\"\n }), \" and the\\ncourier object are filled in.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"There is no scenario for a courier who gives up before pickup. The\\nsame \", _jsx(_components.code, {\n children: \"Idempotency-Key\"\n }), \" with a different \", _jsx(_components.code, {\n children: \"simulate\"\n }), \" is a \", _jsx(_components.code, {\n children: \"409 idempotency_conflict\"\n }), \", like any other changed body, once a request has\\nused the key (it created a delivery, or it is still being processed).\\nA request refused before it created anything, such as the \", _jsx(_components.code, {\n children: \"no_courier\"\n }), \"\\n409, leaves the key free: send it again with a different \", _jsx(_components.code, {\n children: \"simulate\"\n }), \",\\nor none, and it is a new request. A \", _jsx(_components.code, {\n children: \"returned\"\n }), \" delivery cannot be\\nretried (\", _jsx(_components.code, {\n children: \"409 not_retriable\"\n }), \"): its order ended.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A cart too large for one courier trip is refused with \", _jsx(_components.code, {\n children: \"422 order_too_large\"\n }), \", naming \", _jsx(_components.code, {\n children: \"manifest.items\"\n }), \", and nothing is dispatched or\\ndebited. Split it across two deliveries.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Listo delivers in Mexico. A store that turns out to be somewhere else is\\nrefused with \", _jsx(_components.code, {\n children: \"422 unsupported_country\"\n }), \" and nothing is dispatched or\\ndebited; it is final rather than retryable, and the same store will be\\nrefused every time.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A store whose pickup point we cannot currently vouch for is refused\\nwith \", _jsx(_components.code, {\n children: \"409 pickup_point_pending\"\n }), \", and nothing is dispatched or debited:\\nuntil it is confirmed a courier could be sent to the wrong place.\\nUsually the store’s address has just changed and the confirmation has\\nnot landed yet, but not always, so do not read it as “the merchant\\nmoved”. Unlike the refusal above it this one IS retryable, because\\nnothing about your request is wrong and the identical call succeeds once\\nthe pickup point is confirmed.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Retry it on a minutes-scale backoff, for a bounded number of attempts,\\nand then put it in front of a human rather than keep polling. Most moves\\nland within minutes on their own; a few are held up by something only\\nour staff can clear and take considerably longer, and this response does\\nnot tell the two apart. There is no \", _jsx(_components.code, {\n children: \"Retry-After\"\n }), \" on it for that reason:\\nwe would be inventing the number. The restaurant can also unblock it\\nthemselves from their own Listo console, which is usually the faster of\\nthe two.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"When every courier near the trip is busy as the create prices it, the\\ncreate is refused with \", _jsx(_components.code, {\n children: \"409 no_couriers_available\"\n }), \", and nothing is\\ndispatched or debited. It is retryable: repeat the identical request,\\nwith the same \", _jsx(_components.code, {\n children: \"Idempotency-Key\"\n }), \", after a few minutes, for a bounded\\nnumber of attempts. Nothing was recorded under that key, so the repeat\\ncreates the order once a courier is free rather than replaying the\\nrefusal.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A live store whose Plan Listo is not paid up is refused with \", _jsx(_components.code, {\n children: \"402 plan_required\"\n }), \", and nothing is dispatched or debited. It is a different\\nrefusal from \", _jsx(_components.code, {\n children: \"402 insufficient_balance\"\n }), \", which is about your balance:\\nread \", _jsx(_components.code, {\n children: \"error.code\"\n }), \", never the status alone. \", _jsx(_components.code, {\n children: \"error.message\"\n }), \" names the\\nreason (no active plan, a charge that is pending, a plan that ended on a\\ngiven date, a plan whose included deliveries for the cycle are all used,\\nor a store that cannot request couriers right now), and it\\nis not \", _jsx(_components.code, {\n children: \"retryable\"\n }), \". See\\n\", _jsx(_components.a, {\n href: \"https://docs.listo.delivery/en/guides/plan-and-billing\",\n children: \"Plan and billing\"\n }), \".\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A dropoff beyond the store’s 15 km service area is refused with \", _jsx(_components.code, {\n children: \"422 out_of_service_area\"\n }), \", which is final. The courier network can also\\nrefuse the distance of a dropoff at the very edge of that circle, and\\nthe answer is the same \", _jsx(_components.code, {\n children: \"422\"\n }), \", whether it refuses on the price or on the\\ncreate itself. On the create it comes after the delivery was recorded,\\nso the delivery stays, ended at \", _jsx(_components.code, {\n children: \"no_courier\"\n }), \" with \", _jsx(_components.code, {\n children: \"failure_reason: delivery_failed\"\n }), \" under your \", _jsx(_components.code, {\n children: \"external_id\"\n }), \", its fee debit returned in\\nfull and nothing else charged; replaying the request answers the same\\n\", _jsx(_components.code, {\n children: \"422\"\n }), \". Send that order to another address under a new \", _jsx(_components.code, {\n children: \"external_id\"\n }), \".\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Create a delivery","description":"Dispatches a delivery.\n\n`Idempotency-Key` is required. Send a value you generate per order, and\nretry the same call with the same key if you never saw a response: a\nreplay within 24 hours returns the original response, status code\nincluded, without dispatching a second courier or debiting a second\ntime. The same key with a different body is a `409`, and so is a\nrepeat sent while the original request is still being processed\n(`idempotency_conflict` both times). That repeat created nothing and\ndoes not mean the original failed: once the original has answered,\nthe same request with the same key replays its answer.\n\n`dropoff.phone` is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with `validation_failed`.\n\n`external_id` is your own order reference. It is unique per key world,\nechoed in every payload and webhook, and filterable on the list, so you\ncan reconcile without ever storing our ids.\n\n`pickup_ready_at` schedules the pickup for a future moment, between 10\nminutes and 29 days out. Absent, the order dispatches now.\n\nHow the customer pays is `payment_method`. `cash` has the courier\ncollect at the door, up to a cap. `card` mints a branded payment link\nin `payment.url`, which Listo sends your customer itself by WhatsApp\n(and by SMS when the WhatsApp does not reach them), so you do not need\nto send it; the order holds at `awaiting_payment` until the customer\npays. `prepaid` means you have\nalready been paid and the order dispatches immediately.\n\nOn a `prepaid` create that binds no quote, `business_fee_share` is\nforced to 100 and `business_fee_cap_cents` is ignored, whatever you\nsend: you have already collected from your customer, so the whole fee\nis yours. A create that binds a quote is held to the split the quote\nwas priced with, so request the quote with `payment_method: prepaid`\nor with no `payment_method`: either is always priced at share 100 and\nbinds a `prepaid` create. Any other quote is refused with `422\nvalidation_failed`, and the first thing that does not match is the one\nnamed. A quote requested with another `payment_method` (`cash` or\n`card`) is refused naming `payment_method`. A quote whose split is not\nthe one a `prepaid` order has (share 100, no ceiling) is refused naming\n`business_fee_share` or `business_fee_cap_cents`, and `error.message`\nsays why and what to send instead: a new quote requested with\n`payment_method: prepaid`.\n\n`vehicle` is `moto` (the default) or `car`, and `manifest.items[].size`\nis `small` (the default), `medium`, `large` or `xlarge`. A trip goes by\ncar when you ask for one or when any item is sized `xlarge`, and for no\nother reason. A car is a sedan, never a van, can take longer to assign,\nand adds a surcharge inside `fee_cents`. The delivery's `vehicle` says\nwhat the trip was priced and dispatched as; `courier.vehicle_type` is\nwhat actually arrived, which can be a car on a `moto` trip without the\nfee changing.\n\nA create that binds a quote is held to the vehicle the quote was priced\nas. Leave `vehicle` out and it inherits the quote's. Say `moto` against\na car quote, `car` against a moto quote, or send an item sized `xlarge`\nagainst a moto quote, and it is refused with `422 validation_failed`\nnaming `vehicle` (or `manifest.items` when the sizes are what\ndisagree). Re-quote with the cart you mean to send.\n\n`simulate` rehearses what goes wrong, and it works on a test key only.\nA live key that sends it is refused with `422 validation_failed`\nnaming `simulate`, and so is a value that is not one of the three\nbelow. Leave it out and the delivery plays the normal happy path.\n\n- `returned`: the courier reaches the door, cannot hand the order\n over and brings it back. The delivery goes to `returning` and ends\n at `returned` with `failure_reason: customer_unavailable`, and the\n return is billed as a live one is: one `return_fee` entry in\n `charges` covers both legs (see `charges`).\n- `no_courier`: the create answers the same `409\n no_couriers_available` a live create answers when every courier is\n busy, and nothing is created, charged or recorded under your\n `Idempotency-Key`. There is no delivery to retry; repeat the call.\n- `delayed_pickup`: the courier is late. Pickup happens about 25\n minutes after the create and the drop-off about 5 minutes after\n that, counted from `pickup_ready_at` instead when you sent one (the\n delivery waits in `scheduled` until then). It goes through the\n same statuses as the normal run.\n\nIn test mode a delivery may go from `dispatching` straight to\n`picking_up` without ever showing a `courier_assigned` status, in\nevery scenario. Do not wait for it: `courier_assigned_at` and the\ncourier object are filled in.\n\nThere is no scenario for a courier who gives up before pickup. The\nsame `Idempotency-Key` with a different `simulate` is a `409\nidempotency_conflict`, like any other changed body, once a request has\nused the key (it created a delivery, or it is still being processed).\nA request refused before it created anything, such as the `no_courier`\n409, leaves the key free: send it again with a different `simulate`,\nor none, and it is a new request. A `returned` delivery cannot be\nretried (`409 not_retriable`): its order ended.\n\nA cart too large for one courier trip is refused with `422\norder_too_large`, naming `manifest.items`, and nothing is dispatched or\ndebited. Split it across two deliveries.\n\nListo delivers in Mexico. A store that turns out to be somewhere else is\nrefused with `422 unsupported_country` and nothing is dispatched or\ndebited; it is final rather than retryable, and the same store will be\nrefused every time.\n\nA store whose pickup point we cannot currently vouch for is refused\nwith `409 pickup_point_pending`, and nothing is dispatched or debited:\nuntil it is confirmed a courier could be sent to the wrong place.\nUsually the store's address has just changed and the confirmation has\nnot landed yet, but not always, so do not read it as \"the merchant\nmoved\". Unlike the refusal above it this one IS retryable, because\nnothing about your request is wrong and the identical call succeeds once\nthe pickup point is confirmed.\n\nRetry it on a minutes-scale backoff, for a bounded number of attempts,\nand then put it in front of a human rather than keep polling. Most moves\nland within minutes on their own; a few are held up by something only\nour staff can clear and take considerably longer, and this response does\nnot tell the two apart. There is no `Retry-After` on it for that reason:\nwe would be inventing the number. The restaurant can also unblock it\nthemselves from their own Listo console, which is usually the faster of\nthe two.\n\nWhen every courier near the trip is busy as the create prices it, the\ncreate is refused with `409 no_couriers_available`, and nothing is\ndispatched or debited. It is retryable: repeat the identical request,\nwith the same `Idempotency-Key`, after a few minutes, for a bounded\nnumber of attempts. Nothing was recorded under that key, so the repeat\ncreates the order once a courier is free rather than replaying the\nrefusal.\n\nA live store whose Plan Listo is not paid up is refused with `402\nplan_required`, and nothing is dispatched or debited. It is a different\nrefusal from `402 insufficient_balance`, which is about your balance:\nread `error.code`, never the status alone. `error.message` names the\nreason (no active plan, a charge that is pending, a plan that ended on a\ngiven date, a plan whose included deliveries for the cycle are all used,\nor a store that cannot request couriers right now), and it\nis not `retryable`. See\n[Plan and billing](https://docs.listo.delivery/en/guides/plan-and-billing).\n\nA dropoff beyond the store's 15 km service area is refused with `422\nout_of_service_area`, which is final. The courier network can also\nrefuse the distance of a dropoff at the very edge of that circle, and\nthe answer is the same `422`, whether it refuses on the price or on the\ncreate itself. On the create it comes after the delivery was recorded,\nso the delivery stays, ended at `no_courier` with `failure_reason:\ndelivery_failed` under your `external_id`, its fee debit returned in\nfull and nothing else charged; replaying the request answers the same\n`422`. Send that order to another address under a new `external_id`.\n","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/deliveries","href":"/api-reference/deliveries/create-a-delivery","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"summary":"Create a delivery","description":"Dispatches a delivery.\n\n`Idempotency-Key` is required. Send a value you generate per order, and\nretry the same call with the same key if you never saw a response: a\nreplay within 24 hours returns the original response, status code\nincluded, without dispatching a second courier or debiting a second\ntime. The same key with a different body is a `409`, and so is a\nrepeat sent while the original request is still being processed\n(`idempotency_conflict` both times). That repeat created nothing and\ndoes not mean the original failed: once the original has answered,\nthe same request with the same key replays its answer.\n\n`dropoff.phone` is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with `validation_failed`.\n\n`external_id` is your own order reference. It is unique per key world,\nechoed in every payload and webhook, and filterable on the list, so you\ncan reconcile without ever storing our ids.\n\n`pickup_ready_at` schedules the pickup for a future moment, between 10\nminutes and 29 days out. Absent, the order dispatches now.\n\nHow the customer pays is `payment_method`. `cash` has the courier\ncollect at the door, up to a cap. `card` mints a branded payment link\nin `payment.url`, which Listo sends your customer itself by WhatsApp\n(and by SMS when the WhatsApp does not reach them), so you do not need\nto send it; the order holds at `awaiting_payment` until the customer\npays. `prepaid` means you have\nalready been paid and the order dispatches immediately.\n\nOn a `prepaid` create that binds no quote, `business_fee_share` is\nforced to 100 and `business_fee_cap_cents` is ignored, whatever you\nsend: you have already collected from your customer, so the whole fee\nis yours. A create that binds a quote is held to the split the quote\nwas priced with, so request the quote with `payment_method: prepaid`\nor with no `payment_method`: either is always priced at share 100 and\nbinds a `prepaid` create. Any other quote is refused with `422\nvalidation_failed`, and the first thing that does not match is the one\nnamed. A quote requested with another `payment_method` (`cash` or\n`card`) is refused naming `payment_method`. A quote whose split is not\nthe one a `prepaid` order has (share 100, no ceiling) is refused naming\n`business_fee_share` or `business_fee_cap_cents`, and `error.message`\nsays why and what to send instead: a new quote requested with\n`payment_method: prepaid`.\n\n`vehicle` is `moto` (the default) or `car`, and `manifest.items[].size`\nis `small` (the default), `medium`, `large` or `xlarge`. A trip goes by\ncar when you ask for one or when any item is sized `xlarge`, and for no\nother reason. A car is a sedan, never a van, can take longer to assign,\nand adds a surcharge inside `fee_cents`. The delivery's `vehicle` says\nwhat the trip was priced and dispatched as; `courier.vehicle_type` is\nwhat actually arrived, which can be a car on a `moto` trip without the\nfee changing.\n\nA create that binds a quote is held to the vehicle the quote was priced\nas. Leave `vehicle` out and it inherits the quote's. Say `moto` against\na car quote, `car` against a moto quote, or send an item sized `xlarge`\nagainst a moto quote, and it is refused with `422 validation_failed`\nnaming `vehicle` (or `manifest.items` when the sizes are what\ndisagree). Re-quote with the cart you mean to send.\n\n`simulate` rehearses what goes wrong, and it works on a test key only.\nA live key that sends it is refused with `422 validation_failed`\nnaming `simulate`, and so is a value that is not one of the three\nbelow. Leave it out and the delivery plays the normal happy path.\n\n- `returned`: the courier reaches the door, cannot hand the order\n over and brings it back. The delivery goes to `returning` and ends\n at `returned` with `failure_reason: customer_unavailable`, and the\n return is billed as a live one is: one `return_fee` entry in\n `charges` covers both legs (see `charges`).\n- `no_courier`: the create answers the same `409\n no_couriers_available` a live create answers when every courier is\n busy, and nothing is created, charged or recorded under your\n `Idempotency-Key`. There is no delivery to retry; repeat the call.\n- `delayed_pickup`: the courier is late. Pickup happens about 25\n minutes after the create and the drop-off about 5 minutes after\n that, counted from `pickup_ready_at` instead when you sent one (the\n delivery waits in `scheduled` until then). It goes through the\n same statuses as the normal run.\n\nIn test mode a delivery may go from `dispatching` straight to\n`picking_up` without ever showing a `courier_assigned` status, in\nevery scenario. Do not wait for it: `courier_assigned_at` and the\ncourier object are filled in.\n\nThere is no scenario for a courier who gives up before pickup. The\nsame `Idempotency-Key` with a different `simulate` is a `409\nidempotency_conflict`, like any other changed body, once a request has\nused the key (it created a delivery, or it is still being processed).\nA request refused before it created anything, such as the `no_courier`\n409, leaves the key free: send it again with a different `simulate`,\nor none, and it is a new request. A `returned` delivery cannot be\nretried (`409 not_retriable`): its order ended.\n\nA cart too large for one courier trip is refused with `422\norder_too_large`, naming `manifest.items`, and nothing is dispatched or\ndebited. Split it across two deliveries.\n\nListo delivers in Mexico. A store that turns out to be somewhere else is\nrefused with `422 unsupported_country` and nothing is dispatched or\ndebited; it is final rather than retryable, and the same store will be\nrefused every time.\n\nA store whose pickup point we cannot currently vouch for is refused\nwith `409 pickup_point_pending`, and nothing is dispatched or debited:\nuntil it is confirmed a courier could be sent to the wrong place.\nUsually the store's address has just changed and the confirmation has\nnot landed yet, but not always, so do not read it as \"the merchant\nmoved\". Unlike the refusal above it this one IS retryable, because\nnothing about your request is wrong and the identical call succeeds once\nthe pickup point is confirmed.\n\nRetry it on a minutes-scale backoff, for a bounded number of attempts,\nand then put it in front of a human rather than keep polling. Most moves\nland within minutes on their own; a few are held up by something only\nour staff can clear and take considerably longer, and this response does\nnot tell the two apart. There is no `Retry-After` on it for that reason:\nwe would be inventing the number. The restaurant can also unblock it\nthemselves from their own Listo console, which is usually the faster of\nthe two.\n\nWhen every courier near the trip is busy as the create prices it, the\ncreate is refused with `409 no_couriers_available`, and nothing is\ndispatched or debited. It is retryable: repeat the identical request,\nwith the same `Idempotency-Key`, after a few minutes, for a bounded\nnumber of attempts. Nothing was recorded under that key, so the repeat\ncreates the order once a courier is free rather than replaying the\nrefusal.\n\nA live store whose Plan Listo is not paid up is refused with `402\nplan_required`, and nothing is dispatched or debited. It is a different\nrefusal from `402 insufficient_balance`, which is about your balance:\nread `error.code`, never the status alone. `error.message` names the\nreason (no active plan, a charge that is pending, a plan that ended on a\ngiven date, a plan whose included deliveries for the cycle are all used,\nor a store that cannot request couriers right now), and it\nis not `retryable`. See\n[Plan and billing](https://docs.listo.delivery/en/guides/plan-and-billing).\n\nA dropoff beyond the store's 15 km service area is refused with `422\nout_of_service_area`, which is final. The courier network can also\nrefuse the distance of a dropoff at the very edge of that circle, and\nthe answer is the same `422`, whether it refuses on the price or on the\ncreate itself. On the create it comes after the delivery was recorded,\nso the delivery stays, ended at `no_courier` with `failure_reason:\ndelivery_failed` under your `external_id`, its fee debit returned in\nfull and nothing else charged; replaying the request answers the same\n`422`. Send that order to another address under a new `external_id`.\n","tags":["Deliveries"],"operationId":"createDelivery","security":["6f36371e-99e1-485d-8391-2569b8d80504"],"parameters":["e11a5050-eac2-45cc-b13d-30dd55d74f18"],"requestBody":"e639e4b4-5a60-4a7c-9e10-5d6e5987cdab","responses":{"201":"809b0e73-92ea-4b6a-9da1-5421de482085","400":"731d1c58-f612-4602-8432-ad96a94167c0","401":"238c7b46-d2a4-48d5-afda-368712d99696","402":"0011abc1-68e3-4749-b36c-042cf37073d3","403":"def0f860-b288-4b6a-bf70-1566887dc9a1","404":"2dc0cdcd-2a89-49b4-b349-f8e657aa2665","409":"be4dca18-ebdf-43cb-ad6c-fd8dd0ba0e99","422":"12e83613-84ad-4024-9745-8494f63a7a55","503":"568dc3b2-1a5d-4c89-b582-ac076dcc320e"},"method":"post","type":"path","servers":["a658606d-cd78-44bb-b1d3-622847e198a1"],"path":"/v1/deliveries","baseUrlOptions":["https://api.listo.delivery"],"title":"Create a delivery","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"e11a5050-eac2-45cc-b13d-30dd55d74f18":{"name":"Idempotency-Key","in":"header","description":"Required: a call without one is refused with `validation_failed`, naming this header. Generate a NEW key for each logical call (a UUID is a good one), at most 255 characters, and send the SAME key when you retry that call after a timeout or a dropped connection. The same key with the same body returns the ORIGINAL response, status code included, instead of doing the work again, for 24 hours. The same key with a different body is refused with `409 idempotency_conflict`, and so is a retry that arrives while the first request is still being processed (try again shortly). A key belongs to your account and to the mode of the API key that sent it (live and test keys never meet), so the same text from two accounts, or from a live and a test key, is two different keys.","required":true,"schema":{"type":"string","isRequired":true,"description":"Required: a call without one is refused with `validation_failed`, naming this header. Generate a NEW key for each logical call (a UUID is a good one), at most 255 characters, and send the SAME key when you retry that call after a timeout or a dropped connection. The same key with the same body returns the ORIGINAL response, status code included, instead of doing the work again, for 24 hours. The same key with a different body is refused with `409 idempotency_conflict`, and so is a retry that arrives while the first request is still being processed (try again shortly). A key belongs to your account and to the mode of the API key that sent it (live and test keys never meet), so the same text from two accounts, or from a live and a test key, is two different keys.","uniqueKey":"","typeLabel":"string"},"example":"order-841-attempt-1"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","description":"The order to dispatch.","properties":{"store_id":{"type":"string","description":"The `st_` id of the store the courier collects from.","isRequired":true,"uniqueKey":"store_id","typeLabel":"string"},"external_id":{"type":"string","maxLength":255,"description":"Your own reference for this order. Echoed back as `external_id`.","isRequired":true,"uniqueKey":"external_id","typeLabel":"string"},"payment_method":{"type":"string","enum":["prepaid","cash","card"],"description":"How the customer pays.","isRequired":true,"uniqueKey":"payment_method","typeLabel":"enum\u003cstring>"},"order_value_cents":{"type":"integer","minimum":0,"description":"The value of the order, in centavos.","isRequired":true,"uniqueKey":"order_value_cents","typeLabel":"integer"},"dropoff":{"type":"object","description":"Where the order goes, and who receives it.","properties":{"name":{"type":"string","description":"Who receives the order.","isRequired":true,"uniqueKey":"dropoff.name","typeLabel":"string"},"phone":{"type":"string","description":"A phone the courier can call, any format that resolves to an E.164 number.","isRequired":true,"uniqueKey":"dropoff.phone","typeLabel":"string"},"street":{"type":"string","description":"The full street address, in one string.","isRequired":true,"uniqueKey":"dropoff.street","typeLabel":"string"},"apt_unit":{"type":"string","uniqueKey":"dropoff.apt_unit","typeLabel":"string"},"lat":{"type":"number","format":"double","minimum":-90,"maximum":90,"description":"Latitude. Send it together with `lng`, or neither.","uniqueKey":"dropoff.lat","typeLabel":"number\u003cdouble>"},"lng":{"type":"number","format":"double","minimum":-180,"maximum":180,"description":"Longitude. Send it together with `lat`, or neither.","uniqueKey":"dropoff.lng","typeLabel":"number\u003cdouble>"},"notes":{"type":"string","maxLength":280,"description":"Instructions for the courier at the door, up to 280 characters.","uniqueKey":"dropoff.notes","typeLabel":"string"}},"required":["name","phone","street"],"isRequired":true,"uniqueKey":"dropoff","typeLabel":"object"},"quote_id":{"type":"string","description":"The `qt_` id of a quote to book at exactly its price. Without one\nthe delivery is priced as it is created.\n","uniqueKey":"quote_id","typeLabel":"string"},"business_fee_share":{"type":"integer","minimum":0,"maximum":100,"description":"The percent of the delivery fee the store pays. Defaults to the\nstore's own, and is forced to `100` on a `prepaid` order.\n","uniqueKey":"business_fee_share","typeLabel":"integer"},"business_fee_cap_cents":{"type":"integer","minimum":0,"description":"The most the store pays toward the fee, in centavos.","uniqueKey":"business_fee_cap_cents","typeLabel":"integer"},"tip_cents":{"type":"integer","minimum":0,"maximum":100000,"description":"A tip for the courier, in centavos, up to 100000. Allowed on a `prepaid` order only.","uniqueKey":"tip_cents","typeLabel":"integer"},"handoff":{"type":"string","enum":["meet_at_door","leave_at_door","meet_outside"],"description":"Where the courier hands the order over. Defaults to `meet_at_door`.","uniqueKey":"handoff","typeLabel":"enum\u003cstring>"},"manifest":{"oneOf":[{"type":"object","description":"The descriptive item list for the courier. Money never comes from it;\nits only effect on the trip is that an `xlarge` item makes it a car.\nAbsent, null, `{}` and `{\"items\": null}` all mean no cart.\n","properties":{"items":{"oneOf":[{"type":"array","items":{"type":"object","description":"One line of the descriptive item list sent to the courier.","properties":{"name":{"type":"string","description":"What the item is.","isRequired":true,"uniqueKey":"manifest.oneOf.0.\\items.oneOf.0.items.[INDEX].name","typeLabel":"string"},"quantity":{"type":"integer","minimum":1,"description":"How many of it. A positive whole number.","isRequired":true,"uniqueKey":"manifest.oneOf.0.\\items.oneOf.0.items.[INDEX].quantity","typeLabel":"integer"},"size":{"oneOf":[{"type":"string","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n","uniqueKey":"manifest.oneOf.0.\\items.oneOf.0.items.[INDEX].size.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n","uniqueKey":"manifest.oneOf.0.\\items.oneOf.0.items.[INDEX].size.oneOf.1","typeLabel":"null"}],"uniqueKey":"manifest.oneOf.0.\\items.oneOf.0.items.[INDEX].size","typeLabel":"enum\u003cstring> | null"}},"required":["name","quantity"],"uniqueKey":"manifest.oneOf.0.\\items.oneOf.0.items.[INDEX]","typeLabel":"object"},"uniqueKey":"manifest.oneOf.0.\\items.oneOf.0","typeLabel":"object[]"},{"type":"null","items":{"type":"object","description":"One line of the descriptive item list sent to the courier.","properties":{"name":{"type":"string","description":"What the item is."},"quantity":{"type":"integer","minimum":1,"description":"How many of it. A positive whole number."},"size":{"oneOf":[{"type":"string","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n"},{"type":"null","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n"}]}},"required":["name","quantity"]},"uniqueKey":"manifest.oneOf.0.\\items.oneOf.1","typeLabel":"null"}],"uniqueKey":"manifest.oneOf.0.\\items","typeLabel":"object[] | null"}},"uniqueKey":"manifest.oneOf.0","typeLabel":"object"},{"type":"null","description":"The descriptive item list for the courier. Money never comes from it;\nits only effect on the trip is that an `xlarge` item makes it a car.\nAbsent, null, `{}` and `{\"items\": null}` all mean no cart.\n","properties":{"items":{"oneOf":[{"type":"array","items":{"type":"object","description":"One line of the descriptive item list sent to the courier.","properties":{"name":{"type":"string","description":"What the item is."},"quantity":{"type":"integer","minimum":1,"description":"How many of it. A positive whole number."},"size":{"oneOf":[{"type":"string","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n"},{"type":"null","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n"}]}},"required":["name","quantity"]}},{"type":"null","items":{"type":"object","description":"One line of the descriptive item list sent to the courier.","properties":{"name":{"type":"string","description":"What the item is."},"quantity":{"type":"integer","minimum":1,"description":"How many of it. A positive whole number."},"size":{"oneOf":[{"type":"string","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n"},{"type":"null","enum":["small","medium","large","xlarge",null],"description":"How big the item is. Leave it out for `small`. An `xlarge` item\nmakes the trip a car.\n"}]}},"required":["name","quantity"]}}]}},"uniqueKey":"manifest.oneOf.1","typeLabel":"null"}],"uniqueKey":"manifest","typeLabel":"object | null"},"verification":{"type":"object","description":"Proof the customer must give the courier.","properties":{"dropoff_pincode":{"type":"boolean","description":"True to require a code at the door. Defaults to false.","uniqueKey":"verification.dropoff_pincode","typeLabel":"boolean"}},"uniqueKey":"verification","typeLabel":"object"},"pickup_ready_at":{"type":"string","format":"date-time","description":"When the order will be ready, ISO-8601 in UTC, between 10 minutes\nand 29 days from now. Leave it out to dispatch now.\n","uniqueKey":"pickup_ready_at","typeLabel":"string\u003cdate-time>"},"vehicle":{"type":"string","enum":["moto","car"],"description":"Defaults to `moto`, or to the quote's when you book one.","uniqueKey":"vehicle","typeLabel":"enum\u003cstring>"},"simulate":{"type":"string","enum":["returned","no_courier","delayed_pickup"],"description":"Test keys only: the scenario the simulated courier plays. A live\nkey that sends it is refused.\n","uniqueKey":"simulate","typeLabel":"enum\u003cstring>"}},"required":["store_id","external_id","payment_method","order_value_cents","dropoff"],"uniqueKey":"","typeLabel":"object"},"example":{"store_id":"st_4bR8kM2wQ7xT1nV5cZ9pLdYe","external_id":"order-841","payment_method":"prepaid","order_value_cents":19600,"business_fee_share":100,"dropoff":{"name":"Ana Torres","phone":"+528112345678","street":"Blvd. Puerta de Hierro 4965, Zapopan, Jalisco 45116","lat":20.709395,"lng":-103.413393,"apt_unit":"Depto 4B","notes":"Torre B, timbre 4"},"handoff":"leave_at_door","manifest":{"items":[]},"verification":{"dropoff_pincode":true},"pickup_ready_at":"2026-07-29T12:16:40Z","quote_id":"qt_5nW3jP8vT2xQ6bM9cZ1sRdKe","business_fee_cap_cents":3000,"tip_cents":500,"vehicle":"car"},"examples":{"Example":{"title":"Example","value":{"store_id":"st_4bR8kM2wQ7xT1nV5cZ9pLdYe","external_id":"order-841","payment_method":"prepaid","order_value_cents":19600,"business_fee_share":100,"dropoff":{"name":"Ana Torres","phone":"+528112345678","street":"Blvd. Puerta de Hierro 4965, Zapopan, Jalisco 45116","lat":20.709395,"lng":-103.413393,"apt_unit":"Depto 4B","notes":"Torre B, timbre 4"},"handoff":"leave_at_door","manifest":{"items":[]},"verification":{"dropoff_pincode":true},"pickup_ready_at":"2026-07-29T12:16:40Z","quote_id":"qt_5nW3jP8vT2xQ6bM9cZ1sRdKe","business_fee_cap_cents":3000,"tip_cents":500,"vehicle":"car"}}},"exampleType":"examples"}},"required":true,"exampleType":"examples"},"responses":{"809b0e73-92ea-4b6a-9da1-5421de482085":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","description":"The shape of a delivery: every field the object can carry, with the\nnullability each one really has. It is the response of every operation\nthat answers a delivery (the create, the fetch, a list row, the\nreschedule, the cancel, the retry and the refund) and the `data` of\nevery full delivery event, so ONE type holds all of them.\n\nEvery operation returns the whole object. A field that a given call\ncannot yet have is present and null rather than missing: a create\nresponse has a null `courier`, because no courier has been assigned\nyet, and a reschedule only ever answers with a still-scheduled order,\nso its `delivered_at` is null.\n\nMoney is integer centavos, timestamps are ISO-8601 in UTC, and every\nenum below is ours rather than a passthrough of anyone else's.\n","properties":{"id":{"type":"string","description":"The delivery id, `dlv_` prefixed.","example":"dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe","isRequired":true,"uniqueKey":"id","typeLabel":"string"},"external_id":{"oneOf":[{"type":"string","description":"Your own order reference, echoed back exactly as you sent it.","example":"order-841","isRequired":true,"uniqueKey":"external_id.oneOf.0","typeLabel":"string"},{"type":"null","description":"Your own order reference, echoed back exactly as you sent it.","example":"order-841","isRequired":true,"uniqueKey":"external_id.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"external_id","typeLabel":"string | null"},"origin":{"type":"string","enum":["api","console","whatsapp"],"description":"Which lane created the delivery. `api` is one you created through\n`POST /v1/deliveries`. `console` is one the store dispatched itself\nfrom the Listo merchant console, which a store can do only at the\n`full` access tier. `whatsapp` is one a customer ordered from the\nstore in a chat, which the store then accepted; no operator typed\nit anywhere.\n\nAlways present, never null, and fixed for the life of the delivery.\nA console order carries a null `external_id`, but read this field\nrather than that null: the missing reference is a consequence of the\nlane, not a way of identifying it.\n","example":"api","isRequired":true,"uniqueKey":"origin","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"The human order code the courier and our support quote. Unique per\nstore, not globally, so never address an order by it.\n","example":"LS-2048","isRequired":true,"uniqueKey":"code","typeLabel":"string"},"status":{"type":"string","enum":["awaiting_payment","awaiting_location","scheduled","dispatching","courier_assigned","picking_up","en_route","returning","delivered","returned","canceled","no_courier"],"description":"`delivered`, `returned` and `canceled` are terminal. `no_courier` is\na dead end you can retry or cancel. A `returned` delivery is this\nsame object throughout; there is no second delivery for the return\nleg.\n\n`returning`: the drop-off failed and the courier is bringing the\norder back to the store; it ends in `returned`. It is NOT terminal,\nand it is where a failed delivery now spends the minutes between the\ncourier giving up at the customer's door and the order actually\narriving back. `returned` means the order is back. Treat `returning`\nas a live delivery: the courier is still moving and\n`delivery.courier_location` events keep arriving for it.\n\nThe return charge is posted when the delivery enters `returning`,\nnot when it reaches `returned`, so `charges` is already complete on\nthe `returning` event.\n\n`awaiting_location`: an order the store took through Listo's console\nor its WhatsApp assistant, held for the drop-off location. No\ncourier is requested yet. The customer confirms the location on a\nmap link or by sharing it in the WhatsApp chat, or the store\nconfirms it from its console. The delivery then moves to\n`scheduled` or `dispatching`; to `no_courier` when a courier\ncannot be requested for it (the location is outside where the\nstore delivers, or the store itself cannot request a courier right\nnow); or to `canceled` if the order is canceled or the store\nchanges it to a pickup at the counter. A delivery you create\nthrough this API never sits in it.\n","isRequired":true,"uniqueKey":"status","typeLabel":"enum\u003cstring>"},"store_id":{"type":"string","description":"The `st_` id of the store the courier collects from.","isRequired":true,"uniqueKey":"store_id","typeLabel":"string"},"test":{"type":"boolean","description":"Whether this order lives in the sandbox. Derived from the store, so\nthere is no field to set: a test key can only reach test stores.\n","isRequired":true,"uniqueKey":"test","typeLabel":"boolean"},"payment":{"type":"object","description":"How the customer pays, and where that got to.","properties":{"method":{"type":"string","enum":["prepaid","cash","card"],"isRequired":true,"uniqueKey":"payment.method","typeLabel":"enum\u003cstring>"},"status":{"type":"string","enum":["unpaid","awaiting_payment","paid","partially_refunded","refunded"],"isRequired":true,"uniqueKey":"payment.status","typeLabel":"enum\u003cstring>"},"url":{"oneOf":[{"type":"string","format":"uri","description":"The branded payment link for a card order still awaiting\npayment. Listo sends it to your customer itself, by WhatsApp\nand by SMS when the WhatsApp does not reach them, so you can\nshow it or keep it but need not send it. Null the moment the\norder is paid or cancelled, because the link is then dead.\n","isRequired":true,"uniqueKey":"payment.url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"The branded payment link for a card order still awaiting\npayment. Listo sends it to your customer itself, by WhatsApp\nand by SMS when the WhatsApp does not reach them, so you can\nshow it or keep it but need not send it. Null the moment the\norder is paid or cancelled, because the link is then dead.\n","isRequired":true,"uniqueKey":"payment.url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"payment.url","typeLabel":"string\u003curi> | null"},"cash_to_collect_cents":{"oneOf":[{"type":"integer","description":"What the courier collects at the door: the goods value plus the\ncustomer's share of the fee. Null, not zero, on any order that\nis not cash. Zero is a real value on a cash order whose door\ntotal is zero (a `0` order value you fully cover); such an order\nis dispatched with no collection to make.\n","isRequired":true,"uniqueKey":"payment.cash_to_collect_cents.oneOf.0","typeLabel":"integer"},{"type":"null","description":"What the courier collects at the door: the goods value plus the\ncustomer's share of the fee. Null, not zero, on any order that\nis not cash. Zero is a real value on a cash order whose door\ntotal is zero (a `0` order value you fully cover); such an order\nis dispatched with no collection to make.\n","isRequired":true,"uniqueKey":"payment.cash_to_collect_cents.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"payment.cash_to_collect_cents","typeLabel":"integer | null"},"paid_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"payment.paid_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"payment.paid_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"payment.paid_at","typeLabel":"string\u003cdate-time> | null"}},"required":["method","status","url","cash_to_collect_cents","paid_at"],"isRequired":true,"uniqueKey":"payment","typeLabel":"object"},"order_value_cents":{"oneOf":[{"type":"integer","description":"The goods value you sent, untouched.","isRequired":true,"uniqueKey":"order_value_cents.oneOf.0","typeLabel":"integer"},{"type":"null","description":"The goods value you sent, untouched.","isRequired":true,"uniqueKey":"order_value_cents.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"order_value_cents","typeLabel":"integer | null"},"fee_cents":{"type":"integer","description":"The total delivery fee.","isRequired":true,"uniqueKey":"fee_cents","typeLabel":"integer"},"business_fee_cents":{"type":"integer","description":"The part of `fee_cents` charged to you.","isRequired":true,"uniqueKey":"business_fee_cents","typeLabel":"integer"},"customer_fee_cents":{"type":"integer","description":"The part of `fee_cents` charged to the customer. Always sums with\n`business_fee_cents` back to `fee_cents`.\n","isRequired":true,"uniqueKey":"customer_fee_cents","typeLabel":"integer"},"tip_cents":{"type":"integer","description":"The courier tip you sent on this delivery, paid to the courier and\npassed on at cost with no margin taken. Deliberately OUTSIDE\n`fee_cents`, which is the delivery fee `business_fee_cents` and\n`customer_fee_cents` sum back to. 0 when you sent none, and never\nnull.\n","isRequired":true,"uniqueKey":"tip_cents","typeLabel":"integer"},"currency":{"type":"string","enum":["MXN"],"isRequired":true,"uniqueKey":"currency","typeLabel":"enum\u003cstring>"},"quote_id":{"oneOf":[{"type":"string","description":"The `qt_` quote this order bound, so you can tie the price you were\nquoted to the order that spent it. Null when the order was priced\ninline instead.\n","isRequired":true,"uniqueKey":"quote_id.oneOf.0","typeLabel":"string"},{"type":"null","description":"The `qt_` quote this order bound, so you can tie the price you were\nquoted to the order that spent it. Null when the order was priced\ninline instead.\n","isRequired":true,"uniqueKey":"quote_id.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"quote_id","typeLabel":"string | null"},"pickup":{"type":"object","description":"Where the courier collects, snapshotted at create, so a historical\norder keeps reporting where the courier actually went even after the\nstore is renamed or moved.\n","properties":{"name":{"type":"string","isRequired":true,"uniqueKey":"pickup.name","typeLabel":"string"},"street":{"type":"string","isRequired":true,"uniqueKey":"pickup.street","typeLabel":"string"},"lat":{"oneOf":[{"type":"number","format":"double","isRequired":true,"uniqueKey":"pickup.lat.oneOf.0","typeLabel":"number\u003cdouble>"},{"type":"null","format":"double","isRequired":true,"uniqueKey":"pickup.lat.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"pickup.lat","typeLabel":"number\u003cdouble> | null"},"lng":{"oneOf":[{"type":"number","format":"double","isRequired":true,"uniqueKey":"pickup.lng.oneOf.0","typeLabel":"number\u003cdouble>"},{"type":"null","format":"double","isRequired":true,"uniqueKey":"pickup.lng.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"pickup.lng","typeLabel":"number\u003cdouble> | null"},"notes":{"oneOf":[{"type":"string","description":"The store's standing pickup instructions, not a per-order value.","isRequired":true,"uniqueKey":"pickup.notes.oneOf.0","typeLabel":"string"},{"type":"null","description":"The store's standing pickup instructions, not a per-order value.","isRequired":true,"uniqueKey":"pickup.notes.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"pickup.notes","typeLabel":"string | null"}},"required":["name","street","lat","lng","notes"],"isRequired":true,"uniqueKey":"pickup","typeLabel":"object"},"dropoff":{"type":"object","description":"Where the courier delivers, echoed back as you sent it.","properties":{"name":{"type":"string","isRequired":true,"uniqueKey":"dropoff.name","typeLabel":"string"},"phone":{"type":"string","description":"E.164, normalized on the way in.","example":"+528112345678","isRequired":true,"uniqueKey":"dropoff.phone","typeLabel":"string"},"street":{"type":"string","isRequired":true,"uniqueKey":"dropoff.street","typeLabel":"string"},"apt_unit":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"dropoff.apt_unit.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"dropoff.apt_unit.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"dropoff.apt_unit","typeLabel":"string | null"},"lat":{"oneOf":[{"type":"number","format":"double","description":"Null when the address was never geocoded and the text was judged\ninstead. Never a fabricated zero.\n","isRequired":true,"uniqueKey":"dropoff.lat.oneOf.0","typeLabel":"number\u003cdouble>"},{"type":"null","format":"double","description":"Null when the address was never geocoded and the text was judged\ninstead. Never a fabricated zero.\n","isRequired":true,"uniqueKey":"dropoff.lat.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"dropoff.lat","typeLabel":"number\u003cdouble> | null"},"lng":{"oneOf":[{"type":"number","format":"double","isRequired":true,"uniqueKey":"dropoff.lng.oneOf.0","typeLabel":"number\u003cdouble>"},{"type":"null","format":"double","isRequired":true,"uniqueKey":"dropoff.lng.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"dropoff.lng","typeLabel":"number\u003cdouble> | null"},"notes":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"dropoff.notes.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"dropoff.notes.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"dropoff.notes","typeLabel":"string | null"},"handoff":{"type":"string","enum":["meet_at_door","leave_at_door","meet_outside"],"isRequired":true,"uniqueKey":"dropoff.handoff","typeLabel":"enum\u003cstring>"}},"required":["name","phone","street","apt_unit","lat","lng","notes","handoff"],"isRequired":true,"uniqueKey":"dropoff","typeLabel":"object"},"manifest":{"oneOf":[{"type":"object","description":"The descriptive item list you sent, for the courier. Money never\ncomes from it, with one exception: an item sized `xlarge` makes the\ntrip a car. Null when you sent none.\n","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","isRequired":true,"uniqueKey":"manifest.oneOf.0.\\items.items.[INDEX].name","typeLabel":"string"},"quantity":{"type":"integer","isRequired":true,"uniqueKey":"manifest.oneOf.0.\\items.items.[INDEX].quantity","typeLabel":"integer"},"size":{"type":"string","enum":["small","medium","large","xlarge"],"description":"What the item was sent as. `small` when you sent none,\nand on every item created before sizes existed, which is\nwhat those items were sent as.\n","isRequired":true,"uniqueKey":"manifest.oneOf.0.\\items.items.[INDEX].size","typeLabel":"enum\u003cstring>"}},"required":["name","quantity","size"],"uniqueKey":"manifest.oneOf.0.\\items.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"manifest.oneOf.0.\\items","typeLabel":"object[]"}},"required":["items"],"isRequired":true,"uniqueKey":"manifest.oneOf.0","typeLabel":"object"},{"type":"null","description":"The descriptive item list you sent, for the courier. Money never\ncomes from it, with one exception: an item sized `xlarge` makes the\ntrip a car. Null when you sent none.\n","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"integer"},"size":{"type":"string","enum":["small","medium","large","xlarge"],"description":"What the item was sent as. `small` when you sent none,\nand on every item created before sizes existed, which is\nwhat those items were sent as.\n"}},"required":["name","quantity","size"]}}},"required":["items"],"isRequired":true,"uniqueKey":"manifest.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"manifest","typeLabel":"object | null"},"vehicle":{"type":"string","enum":["moto","car"],"description":"What the trip was priced and dispatched as: `car` when you asked for\none or when an item was sized `xlarge`, `moto` otherwise. A car's\nsurcharge is inside `fee_cents`. Always present, never null, and\nfixed for the life of the delivery.\n\nNot the vehicle that arrived. That is `courier.vehicle_type`, which\ncan be a car on a `moto` trip without the fee changing.\n","example":"moto","isRequired":true,"uniqueKey":"vehicle","typeLabel":"enum\u003cstring>"},"courier":{"oneOf":[{"type":"object","description":"Null until a courier is assigned, and then FROZEN: a terminal\ndelivery keeps reporting who carried it.\n","properties":{"name":{"type":"string","isRequired":true,"uniqueKey":"courier.oneOf.0.name","typeLabel":"string"},"vehicle":{"oneOf":[{"type":"string","description":"What the courier is riding, in English whatever locale the\nrequest carried (\"Motorcycle\", \"Bicycle\"). Derived from\n`vehicle_type`, so it is null exactly when that is: display it,\nand switch on `vehicle_type` instead.\n","isRequired":true,"uniqueKey":"courier.oneOf.0.vehicle.oneOf.0","typeLabel":"string"},{"type":"null","description":"What the courier is riding, in English whatever locale the\nrequest carried (\"Motorcycle\", \"Bicycle\"). Derived from\n`vehicle_type`, so it is null exactly when that is: display it,\nand switch on `vehicle_type` instead.\n","isRequired":true,"uniqueKey":"courier.oneOf.0.vehicle.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.vehicle","typeLabel":"string | null"},"vehicle_type":{"oneOf":[{"type":"string","description":"The machine-readable type: `bicycle`, `car`, `van`, `truck`,\n`scooter`, `motorcycle` or `walker`. Null until the courier is\nassigned a vehicle. Treat an unlisted value as unknown rather\nthan an error: this list grows.\n","isRequired":true,"uniqueKey":"courier.oneOf.0.vehicle_type.oneOf.0","typeLabel":"string"},{"type":"null","description":"The machine-readable type: `bicycle`, `car`, `van`, `truck`,\n`scooter`, `motorcycle` or `walker`. Null until the courier is\nassigned a vehicle. Treat an unlisted value as unknown rather\nthan an error: this list grows.\n","isRequired":true,"uniqueKey":"courier.oneOf.0.vehicle_type.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.vehicle_type","typeLabel":"string | null"},"plate":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"courier.oneOf.0.plate.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"courier.oneOf.0.plate.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.plate","typeLabel":"string | null"},"phone":{"oneOf":[{"type":"object","description":"A masked number and its IVR PIN, never a personal number. Dial\n`masked` and enter `pin` when the system asks. Null when no\nnumber has been issued for this courier.\n","properties":{"masked":{"type":"string","isRequired":true,"uniqueKey":"courier.oneOf.0.phone.oneOf.0.masked","typeLabel":"string"},"pin":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"courier.oneOf.0.phone.oneOf.0.pin.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"courier.oneOf.0.phone.oneOf.0.pin.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.phone.oneOf.0.pin","typeLabel":"string | null"}},"required":["masked","pin"],"isRequired":true,"uniqueKey":"courier.oneOf.0.phone.oneOf.0","typeLabel":"object"},{"type":"null","description":"A masked number and its IVR PIN, never a personal number. Dial\n`masked` and enter `pin` when the system asks. Null when no\nnumber has been issued for this courier.\n","properties":{"masked":{"type":"string"},"pin":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["masked","pin"],"isRequired":true,"uniqueKey":"courier.oneOf.0.phone.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.phone","typeLabel":"object | null"},"location":{"oneOf":[{"type":"object","description":"The live position, present only while the courier is actually in\nmotion and only once there is a real fix.\n","properties":{"lat":{"type":"number","format":"double","isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.0.lat","typeLabel":"number\u003cdouble>"},"lng":{"type":"number","format":"double","isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.0.lng","typeLabel":"number\u003cdouble>"},"updated_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.0.updated_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.0.updated_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.0.updated_at","typeLabel":"string\u003cdate-time> | null"}},"required":["lat","lng","updated_at"],"isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.0","typeLabel":"object"},{"type":"null","description":"The live position, present only while the courier is actually in\nmotion and only once there is a real fix.\n","properties":{"lat":{"type":"number","format":"double"},"lng":{"type":"number","format":"double"},"updated_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null","format":"date-time"}]}},"required":["lat","lng","updated_at"],"isRequired":true,"uniqueKey":"courier.oneOf.0.location.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier.oneOf.0.location","typeLabel":"object | null"},"imminent":{"type":"boolean","description":"The courier is about a minute away. Always false once the trip ends.","isRequired":true,"uniqueKey":"courier.oneOf.0.imminent","typeLabel":"boolean"}},"required":["name","vehicle","vehicle_type","plate","phone","location","imminent"],"isRequired":true,"uniqueKey":"courier.oneOf.0","typeLabel":"object"},{"type":"null","description":"Null until a courier is assigned, and then FROZEN: a terminal\ndelivery keeps reporting who carried it.\n","properties":{"name":{"type":"string"},"vehicle":{"oneOf":[{"type":"string","description":"What the courier is riding, in English whatever locale the\nrequest carried (\"Motorcycle\", \"Bicycle\"). Derived from\n`vehicle_type`, so it is null exactly when that is: display it,\nand switch on `vehicle_type` instead.\n"},{"type":"null","description":"What the courier is riding, in English whatever locale the\nrequest carried (\"Motorcycle\", \"Bicycle\"). Derived from\n`vehicle_type`, so it is null exactly when that is: display it,\nand switch on `vehicle_type` instead.\n"}]},"vehicle_type":{"oneOf":[{"type":"string","description":"The machine-readable type: `bicycle`, `car`, `van`, `truck`,\n`scooter`, `motorcycle` or `walker`. Null until the courier is\nassigned a vehicle. Treat an unlisted value as unknown rather\nthan an error: this list grows.\n"},{"type":"null","description":"The machine-readable type: `bicycle`, `car`, `van`, `truck`,\n`scooter`, `motorcycle` or `walker`. Null until the courier is\nassigned a vehicle. Treat an unlisted value as unknown rather\nthan an error: this list grows.\n"}]},"plate":{"oneOf":[{"type":"string"},{"type":"null"}]},"phone":{"oneOf":[{"type":"object","description":"A masked number and its IVR PIN, never a personal number. Dial\n`masked` and enter `pin` when the system asks. Null when no\nnumber has been issued for this courier.\n","properties":{"masked":{"type":"string"},"pin":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["masked","pin"]},{"type":"null","description":"A masked number and its IVR PIN, never a personal number. Dial\n`masked` and enter `pin` when the system asks. Null when no\nnumber has been issued for this courier.\n","properties":{"masked":{"type":"string"},"pin":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["masked","pin"]}]},"location":{"oneOf":[{"type":"object","description":"The live position, present only while the courier is actually in\nmotion and only once there is a real fix.\n","properties":{"lat":{"type":"number","format":"double"},"lng":{"type":"number","format":"double"},"updated_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null","format":"date-time"}]}},"required":["lat","lng","updated_at"]},{"type":"null","description":"The live position, present only while the courier is actually in\nmotion and only once there is a real fix.\n","properties":{"lat":{"type":"number","format":"double"},"lng":{"type":"number","format":"double"},"updated_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null","format":"date-time"}]}},"required":["lat","lng","updated_at"]}]},"imminent":{"type":"boolean","description":"The courier is about a minute away. Always false once the trip ends."}},"required":["name","vehicle","vehicle_type","plate","phone","location","imminent"],"isRequired":true,"uniqueKey":"courier.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier","typeLabel":"object | null"},"pickup_eta_at":{"oneOf":[{"type":"string","format":"date-time","description":"When the courier is expected at the store. Absolute, so you compute\nyour own countdown; a countdown computed here is stale before you\nread it.\n","isRequired":true,"uniqueKey":"pickup_eta_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When the courier is expected at the store. Absolute, so you compute\nyour own countdown; a countdown computed here is stale before you\nread it.\n","isRequired":true,"uniqueKey":"pickup_eta_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"pickup_eta_at","typeLabel":"string\u003cdate-time> | null"},"dropoff_eta_at":{"oneOf":[{"type":"string","format":"date-time","description":"When the courier is expected at the customer. Null until there is\nan estimate, and null again once the drop-off has failed\n(`returning`, `returned`): the courier is then bringing the order\nback to the store, there is no arrival at the customer left to\ndescribe, and the time it gets back to the store is never published\nhere.\n","isRequired":true,"uniqueKey":"dropoff_eta_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When the courier is expected at the customer. Null until there is\nan estimate, and null again once the drop-off has failed\n(`returning`, `returned`): the courier is then bringing the order\nback to the store, there is no arrival at the customer left to\ndescribe, and the time it gets back to the store is never published\nhere.\n","isRequired":true,"uniqueKey":"dropoff_eta_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"dropoff_eta_at","typeLabel":"string\u003cdate-time> | null"},"tracking_url":{"oneOf":[{"type":"string","format":"uri","description":"The branded live tracking page for the customer: the map, the\ncourier, the delivery code and what is owed at the door. Null when\nthere is no page to point at, so you never relay a dead link.\n\nThere is a page from the moment the order is dispatched, including\nthe whole window before a courier is assigned, and for 48 hours\nafter the order is delivered, canceled or returned. It is null while\nan order is still held awaiting payment or awaiting a confirmed\ndrop-off pin, and null again once that 48 hours has passed.\n","example":"https://app.listo.delivery/t/abc123","isRequired":true,"uniqueKey":"tracking_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"The branded live tracking page for the customer: the map, the\ncourier, the delivery code and what is owed at the door. Null when\nthere is no page to point at, so you never relay a dead link.\n\nThere is a page from the moment the order is dispatched, including\nthe whole window before a courier is assigned, and for 48 hours\nafter the order is delivered, canceled or returned. It is null while\nan order is still held awaiting payment or awaiting a confirmed\ndrop-off pin, and null again once that 48 hours has passed.\n","example":"https://app.listo.delivery/t/abc123","isRequired":true,"uniqueKey":"tracking_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"tracking_url","typeLabel":"string\u003curi> | null"},"verification":{"oneOf":[{"type":"object","description":"Present only when the order carries a dropoff PIN: when you asked\nfor one (`verification.dropoff_pincode` on the create), or when\nListo requires a PIN on every delivery of this store's account, in\nwhich case it is present even though you did not ask. `pincode` is\nnull until the order is actually dispatched, which is when the code\nis minted.\n","properties":{"pincode":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"verification.oneOf.0.pincode.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"verification.oneOf.0.pincode.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"verification.oneOf.0.pincode","typeLabel":"string | null"}},"required":["pincode"],"isRequired":true,"uniqueKey":"verification.oneOf.0","typeLabel":"object"},{"type":"null","description":"Present only when the order carries a dropoff PIN: when you asked\nfor one (`verification.dropoff_pincode` on the create), or when\nListo requires a PIN on every delivery of this store's account, in\nwhich case it is present even though you did not ask. `pincode` is\nnull until the order is actually dispatched, which is when the code\nis minted.\n","properties":{"pincode":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["pincode"],"isRequired":true,"uniqueKey":"verification.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"verification","typeLabel":"object | null"},"return_verification":{"type":"object","description":"The return code (código de devolución). When an order cannot be\ndelivered and the courier brings it back to the store, the courier\nmust be given this code at the store to hand the order over; without\nit the return cannot be completed. `pincode` is a 4-digit string\nwhile the delivery is `returning`, and null in every other state\n(before the return, and once it is `returned`). The object is\nalways present, so this field never needs a presence check. It\narrives in `delivery.status_changed` (the move into `returning`) and\nin `delivery.updated`, which carry the whole object. A new attempt\n(a retry) gets its own code. Show it to the store, and never to the\ncustomer.\n","properties":{"pincode":{"oneOf":[{"type":"string","example":"4821","isRequired":true,"uniqueKey":"return_verification.pincode.oneOf.0","typeLabel":"string"},{"type":"null","example":"4821","isRequired":true,"uniqueKey":"return_verification.pincode.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"return_verification.pincode","typeLabel":"string | null"}},"required":["pincode"],"isRequired":true,"uniqueKey":"return_verification","typeLabel":"object"},"proof":{"oneOf":[{"type":"object","description":"Proof of delivery. Null unless we hold some, and each field is null\nwhen that piece is missing; a photo with no signature is normal. The\nURLs are Listo's own signed links to the stored copy and are valid\nfor 24 hours from the response (or webhook) that carried them:\nfetch `GET /v1/deliveries/{id}` again for a fresh link, or keep a\ncopy. **A URL is null until Listo has its own copy of the image**\n(a few seconds after the courier takes it); the copy's arrival is\nannounced by a `delivery.updated` webhook that carries the link.\nIt may come from a delivery attempt that failed at the\ndoor, so it is not by itself proof that the order was handed over:\n`proofs.dropoff.delivered` says which. Prefer `proofs`, which keeps\neach moment of the trip apart.\n","properties":{"picture_url":{"oneOf":[{"type":"string","format":"uri","isRequired":true,"uniqueKey":"proof.oneOf.0.picture_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","isRequired":true,"uniqueKey":"proof.oneOf.0.picture_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proof.oneOf.0.picture_url","typeLabel":"string\u003curi> | null"},"signature_image_url":{"oneOf":[{"type":"string","format":"uri","isRequired":true,"uniqueKey":"proof.oneOf.0.signature_image_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","isRequired":true,"uniqueKey":"proof.oneOf.0.signature_image_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proof.oneOf.0.signature_image_url","typeLabel":"string\u003curi> | null"},"signature_name":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"proof.oneOf.0.signature_name.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"proof.oneOf.0.signature_name.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proof.oneOf.0.signature_name","typeLabel":"string | null"},"captured_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"proof.oneOf.0.captured_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"proof.oneOf.0.captured_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proof.oneOf.0.captured_at","typeLabel":"string\u003cdate-time> | null"}},"required":["picture_url","signature_image_url","signature_name","captured_at"],"isRequired":true,"uniqueKey":"proof.oneOf.0","typeLabel":"object"},{"type":"null","description":"Proof of delivery. Null unless we hold some, and each field is null\nwhen that piece is missing; a photo with no signature is normal. The\nURLs are Listo's own signed links to the stored copy and are valid\nfor 24 hours from the response (or webhook) that carried them:\nfetch `GET /v1/deliveries/{id}` again for a fresh link, or keep a\ncopy. **A URL is null until Listo has its own copy of the image**\n(a few seconds after the courier takes it); the copy's arrival is\nannounced by a `delivery.updated` webhook that carries the link.\nIt may come from a delivery attempt that failed at the\ndoor, so it is not by itself proof that the order was handed over:\n`proofs.dropoff.delivered` says which. Prefer `proofs`, which keeps\neach moment of the trip apart.\n","properties":{"picture_url":{"oneOf":[{"type":"string","format":"uri"},{"type":"null","format":"uri"}]},"signature_image_url":{"oneOf":[{"type":"string","format":"uri"},{"type":"null","format":"uri"}]},"signature_name":{"oneOf":[{"type":"string"},{"type":"null"}]},"captured_at":{"oneOf":[{"type":"string","format":"date-time"},{"type":"null","format":"date-time"}]}},"required":["picture_url","signature_image_url","signature_name","captured_at"],"isRequired":true,"uniqueKey":"proof.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proof","typeLabel":"object | null"},"proofs":{"type":"object","description":"The evidence the trip left, one entry per moment. Each moment is\nnull until it happens (nothing has been captured for it yet), so\nthis object is always present with its three keys. A photo or\nsignature link is Listo's own signed link to the stored copy, and\n**it is null until Listo holds that copy** (a few seconds after the\ncourier takes the photo): the rest of the moment, the signer, the\ntime and the distance, is there from the start, and when the copy\nlands a `delivery.updated` webhook carries the link. **Each link is\nvalid for 24 hours** from the response (or webhook) that carried\nit: it is a short-lived signed link, never a permanent one. A\npartner that needs the image later fetches\n`GET /v1/deliveries/{id}` again for a fresh link, or keeps a copy.\n","properties":{"pickup":{"oneOf":[{"type":"object","description":"The courier collecting the order at the store. Null until the\npickup photo exists.\n","properties":{"photo_url":{"oneOf":[{"type":"string","format":"uri","description":"Photo of the order at the store.","isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0.photo_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"Photo of the order at the store.","isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0.photo_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0.photo_url","typeLabel":"string\u003curi> | null"},"captured_at":{"oneOf":[{"type":"string","format":"date-time","description":"When Listo first saw this photo.","isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0.captured_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When Listo first saw this photo.","isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0.captured_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0.captured_at","typeLabel":"string\u003cdate-time> | null"}},"required":["photo_url","captured_at"],"isRequired":true,"uniqueKey":"proofs.pickup.oneOf.0","typeLabel":"object"},{"type":"null","description":"The courier collecting the order at the store. Null until the\npickup photo exists.\n","properties":{"photo_url":{"oneOf":[{"type":"string","format":"uri","description":"Photo of the order at the store."},{"type":"null","format":"uri","description":"Photo of the order at the store."}]},"captured_at":{"oneOf":[{"type":"string","format":"date-time","description":"When Listo first saw this photo."},{"type":"null","format":"date-time","description":"When Listo first saw this photo."}]}},"required":["photo_url","captured_at"],"isRequired":true,"uniqueKey":"proofs.pickup.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.pickup","typeLabel":"object | null"},"dropoff":{"oneOf":[{"type":"object","description":"The courier at the customer's door. Present once the order is\ndelivered, or once the attempt left a photo, a signature or a\nsigner there, **including an attempt that failed**: read\n`delivered`, not the presence of a photo, to know whether the\norder was handed over.\n","properties":{"photo_url":{"oneOf":[{"type":"string","format":"uri","description":"Photo taken at the door.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.photo_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"Photo taken at the door.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.photo_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.photo_url","typeLabel":"string\u003curi> | null"},"signature_url":{"oneOf":[{"type":"string","format":"uri","description":"Image of the recipient's signature.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.signature_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"Image of the recipient's signature.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.signature_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.signature_url","typeLabel":"string\u003curi> | null"},"signer_name":{"oneOf":[{"type":"string","description":"The name the person who signed typed.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.signer_name.oneOf.0","typeLabel":"string"},{"type":"null","description":"The name the person who signed typed.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.signer_name.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.signer_name","typeLabel":"string | null"},"captured_at":{"oneOf":[{"type":"string","format":"date-time","description":"When Listo first saw this evidence.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.captured_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When Listo first saw this evidence.","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.captured_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.captured_at","typeLabel":"string\u003cdate-time> | null"},"delivered":{"type":"boolean","description":"True only when the delivery was delivered. False means the\nevidence is from an attempt that did not hand the order over.\n","isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0.delivered","typeLabel":"boolean"}},"required":["photo_url","signature_url","signer_name","captured_at","delivered"],"isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.0","typeLabel":"object"},{"type":"null","description":"The courier at the customer's door. Present once the order is\ndelivered, or once the attempt left a photo, a signature or a\nsigner there, **including an attempt that failed**: read\n`delivered`, not the presence of a photo, to know whether the\norder was handed over.\n","properties":{"photo_url":{"oneOf":[{"type":"string","format":"uri","description":"Photo taken at the door."},{"type":"null","format":"uri","description":"Photo taken at the door."}]},"signature_url":{"oneOf":[{"type":"string","format":"uri","description":"Image of the recipient's signature."},{"type":"null","format":"uri","description":"Image of the recipient's signature."}]},"signer_name":{"oneOf":[{"type":"string","description":"The name the person who signed typed."},{"type":"null","description":"The name the person who signed typed."}]},"captured_at":{"oneOf":[{"type":"string","format":"date-time","description":"When Listo first saw this evidence."},{"type":"null","format":"date-time","description":"When Listo first saw this evidence."}]},"delivered":{"type":"boolean","description":"True only when the delivery was delivered. False means the\nevidence is from an attempt that did not hand the order over.\n"}},"required":["photo_url","signature_url","signer_name","captured_at","delivered"],"isRequired":true,"uniqueKey":"proofs.dropoff.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.dropoff","typeLabel":"object | null"},"return":{"oneOf":[{"type":"object","description":"The courier handing the order back at the store after a failed\ndelivery. Null unless the order came back with evidence.\n","properties":{"photo_url":{"oneOf":[{"type":"string","format":"uri","description":"Photo of the order handed back at the store.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.photo_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"Photo of the order handed back at the store.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.photo_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.return.oneOf.0.photo_url","typeLabel":"string\u003curi> | null"},"signature_url":{"oneOf":[{"type":"string","format":"uri","description":"Image of the signature of whoever received it at the store.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.signature_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"Image of the signature of whoever received it at the store.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.signature_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.return.oneOf.0.signature_url","typeLabel":"string\u003curi> | null"},"signer_name":{"oneOf":[{"type":"string","description":"The name the person who signed typed.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.signer_name.oneOf.0","typeLabel":"string"},{"type":"null","description":"The name the person who signed typed.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.signer_name.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.return.oneOf.0.signer_name","typeLabel":"string | null"},"captured_at":{"oneOf":[{"type":"string","format":"date-time","description":"When Listo first saw this evidence.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.captured_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When Listo first saw this evidence.","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.captured_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.return.oneOf.0.captured_at","typeLabel":"string\u003cdate-time> | null"},"meters_from_store":{"oneOf":[{"type":"integer","description":"How far from the store the courier was when this was\ncaptured, in whole metres in a straight line. Null when\neither position is unknown. Evidence for a person to read,\nnot a verdict.\n","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.meters_from_store.oneOf.0","typeLabel":"integer"},{"type":"null","description":"How far from the store the courier was when this was\ncaptured, in whole metres in a straight line. Null when\neither position is unknown. Evidence for a person to read,\nnot a verdict.\n","isRequired":true,"uniqueKey":"proofs.return.oneOf.0.meters_from_store.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.return.oneOf.0.meters_from_store","typeLabel":"integer | null"}},"required":["photo_url","signature_url","signer_name","captured_at","meters_from_store"],"isRequired":true,"uniqueKey":"proofs.return.oneOf.0","typeLabel":"object"},{"type":"null","description":"The courier handing the order back at the store after a failed\ndelivery. Null unless the order came back with evidence.\n","properties":{"photo_url":{"oneOf":[{"type":"string","format":"uri","description":"Photo of the order handed back at the store."},{"type":"null","format":"uri","description":"Photo of the order handed back at the store."}]},"signature_url":{"oneOf":[{"type":"string","format":"uri","description":"Image of the signature of whoever received it at the store."},{"type":"null","format":"uri","description":"Image of the signature of whoever received it at the store."}]},"signer_name":{"oneOf":[{"type":"string","description":"The name the person who signed typed."},{"type":"null","description":"The name the person who signed typed."}]},"captured_at":{"oneOf":[{"type":"string","format":"date-time","description":"When Listo first saw this evidence."},{"type":"null","format":"date-time","description":"When Listo first saw this evidence."}]},"meters_from_store":{"oneOf":[{"type":"integer","description":"How far from the store the courier was when this was\ncaptured, in whole metres in a straight line. Null when\neither position is unknown. Evidence for a person to read,\nnot a verdict.\n"},{"type":"null","description":"How far from the store the courier was when this was\ncaptured, in whole metres in a straight line. Null when\neither position is unknown. Evidence for a person to read,\nnot a verdict.\n"}]}},"required":["photo_url","signature_url","signer_name","captured_at","meters_from_store"],"isRequired":true,"uniqueKey":"proofs.return.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"proofs.return","typeLabel":"object | null"}},"required":["pickup","dropoff","return"],"isRequired":true,"uniqueKey":"proofs","typeLabel":"object"},"cancellation":{"oneOf":[{"type":"object","description":"Who called the order off, and why. Null on an order that was not cancelled.","properties":{"party":{"type":"string","enum":["partner","courier","platform"],"description":"`partner` is you. `courier` is the person carrying it.\n`platform` is us, and it also covers a cancellation reported to\nus on the customer's behalf: the end customer is not an actor in\nthis API, so this field never names a person you cannot\ncorrelate.\n","isRequired":true,"uniqueKey":"cancellation.oneOf.0.party","typeLabel":"enum\u003cstring>"},"reason":{"oneOf":[{"type":"string","enum":["customer_canceled","store_closed","item_unavailable","wrong_address","no_longer_needed","other",null],"description":"The reason you sent on a cancel. Null when the attribution was\nderived rather than given, which is every cancellation you did\nnot make.\n","isRequired":true,"uniqueKey":"cancellation.oneOf.0.reason.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["customer_canceled","store_closed","item_unavailable","wrong_address","no_longer_needed","other",null],"description":"The reason you sent on a cancel. Null when the attribution was\nderived rather than given, which is every cancellation you did\nnot make.\n","isRequired":true,"uniqueKey":"cancellation.oneOf.0.reason.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"cancellation.oneOf.0.reason","typeLabel":"enum\u003cstring> | null"},"description":{"oneOf":[{"type":"string","isRequired":true,"uniqueKey":"cancellation.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","isRequired":true,"uniqueKey":"cancellation.oneOf.0.description.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"cancellation.oneOf.0.description","typeLabel":"string | null"}},"required":["party","reason","description"],"isRequired":true,"uniqueKey":"cancellation.oneOf.0","typeLabel":"object"},{"type":"null","description":"Who called the order off, and why. Null on an order that was not cancelled.","properties":{"party":{"type":"string","enum":["partner","courier","platform"],"description":"`partner` is you. `courier` is the person carrying it.\n`platform` is us, and it also covers a cancellation reported to\nus on the customer's behalf: the end customer is not an actor in\nthis API, so this field never names a person you cannot\ncorrelate.\n"},"reason":{"oneOf":[{"type":"string","enum":["customer_canceled","store_closed","item_unavailable","wrong_address","no_longer_needed","other",null],"description":"The reason you sent on a cancel. Null when the attribution was\nderived rather than given, which is every cancellation you did\nnot make.\n"},{"type":"null","enum":["customer_canceled","store_closed","item_unavailable","wrong_address","no_longer_needed","other",null],"description":"The reason you sent on a cancel. Null when the attribution was\nderived rather than given, which is every cancellation you did\nnot make.\n"}]},"description":{"oneOf":[{"type":"string"},{"type":"null"}]}},"required":["party","reason","description"],"isRequired":true,"uniqueKey":"cancellation.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"cancellation","typeLabel":"object | null"},"failure_reason":{"oneOf":[{"type":"string","enum":["no_couriers_available","address_unreachable","customer_unavailable","delivery_failed",null],"description":"Why a dispatch failed, bucketed by whose side the problem is on, so\nyou can act on it rather than just retry blindly.\n`no_couriers_available` is the retriable one.\n`delivery_failed` is the honest catch-all. Null unless a failure was\nrecorded.\n","isRequired":true,"uniqueKey":"failure_reason.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["no_couriers_available","address_unreachable","customer_unavailable","delivery_failed",null],"description":"Why a dispatch failed, bucketed by whose side the problem is on, so\nyou can act on it rather than just retry blindly.\n`no_couriers_available` is the retriable one.\n`delivery_failed` is the honest catch-all. Null unless a failure was\nrecorded.\n","isRequired":true,"uniqueKey":"failure_reason.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"failure_reason","typeLabel":"enum\u003cstring> | null"},"charges":{"type":"array","description":"What a trip that was driven but wasted was billed at: the delivery\nfee for each leg driven, less your own store markup. Empty on\nalmost every delivery, and never null.\n\n**This amount changed on 2026-08-29.** It used to be what the\nwasted trip cost Listo, so expect larger values on the same orders\nfrom that date onward. The field itself is unchanged.\n\nAlways present on a delivery object, so treat an absent `charges`\nas impossible rather than as \"no charges\". It is in this schema's\n`required` list for that reason.\n\nA charge can arrive on the delivery's final\n`delivery.status_changed` or, when the cost settles afterwards, on\na later `delivery.updated`. You cannot assume which, so read this\non both: billing the moment a delivery reaches its terminal status\nbills short on every order whose cost landed late.\n\nThe same movement appears on `GET /v1/ledger` as a signed row, so\nit reads negative there while `amount_cents` here is positive.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["cancellation_fee","return_fee"],"description":"What the charge was for. `cancellation_fee` is a\ncancellation late enough that a courier was already working\non the order. `return_fee` is a return trip after a failed\ndropoff, and it bills as ONE entry covering both legs of the\njourney rather than two.\n","isRequired":true,"uniqueKey":"charges.items.[INDEX].type","typeLabel":"enum\u003cstring>"},"amount_cents":{"type":"integer","description":"What you were charged, positive, in Mexican peso cents: the\ndelivery fee for each leg that was driven, less your own\nstore markup.\n","isRequired":true,"uniqueKey":"charges.items.[INDEX].amount_cents","typeLabel":"integer"},"charged_at":{"type":"string","format":"date-time","description":"When the charge was recorded, which is not when the delivery\nended.\n","isRequired":true,"uniqueKey":"charges.items.[INDEX].charged_at","typeLabel":"string\u003cdate-time>"}},"required":["type","amount_cents","charged_at"],"uniqueKey":"charges.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"charges","typeLabel":"object[]"},"refunds":{"type":"array","description":"Every time money went back to the customer on this order, newest\nfirst. Empty on an order that has never been refunded, and never\nnull.\n\nAlways present on a delivery object, so treat an absent `refunds`\nas impossible rather than as \"no refunds\". It is in this schema's\n`required` list for that reason, exactly as `charges` above is.\n\nIt is a LIST because refunds repeat: a partial refund is followed\nby another refund, each with its own amount, its own reason and its\nown outcome. Every refund we can see is here whoever made it, which\nis what makes the list worth reconciling against. The connected\naccount belongs to the merchant of record, which can refund a charge\non it without going through us at all; those arrive as\n`requested_via: external`, and a list that showed only the refunds\nyou asked for would silently disagree with this order's own\n`payment.status`.\n\nA refund rides `delivery.updated`, never an event of its own.\n","items":{"type":"object","properties":{"id":{"type":"string","description":"The refund id, `rfn_` prefixed.","example":"rfn_4bQ7wS2xK9mT6vN3cZ8pLdYe","isRequired":true,"uniqueKey":"refunds.items.[INDEX].id","typeLabel":"string"},"amount_cents":{"oneOf":[{"type":"integer","description":"What went back, positive, in Mexican peso cents. NULL AND NOT\nZERO while a refund of the remaining balance is still in\nflight: it carries no amount until the refund is confirmed,\nand a zero would say a refund moved nothing. Read\n`refunded_cents` below rather than summing this array, which\nis exactly why both totals are published.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].amount_cents.oneOf.0","typeLabel":"integer"},{"type":"null","description":"What went back, positive, in Mexican peso cents. NULL AND NOT\nZERO while a refund of the remaining balance is still in\nflight: it carries no amount until the refund is confirmed,\nand a zero would say a refund moved nothing. Read\n`refunded_cents` below rather than summing this array, which\nis exactly why both totals are published.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].amount_cents.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"refunds.items.[INDEX].amount_cents","typeLabel":"integer | null"},"reason":{"oneOf":[{"type":"string","description":"The free text whoever asked for the refund supplied, or null.\nYour own words when you sent them; it is never an enum.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].reason.oneOf.0","typeLabel":"string"},{"type":"null","description":"The free text whoever asked for the refund supplied, or null.\nYour own words when you sent them; it is never an enum.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].reason.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"refunds.items.[INDEX].reason","typeLabel":"string | null"},"status":{"type":"string","enum":["pending","succeeded","failed"],"description":"`pending` IS NOT A FAILURE and must not be retried: a\ndirect-charge refund is held while the account's balance is\nshort and applied once funds land. It flips to `succeeded` on\nits own and publishes that as `delivery.updated`. `failed`\nmeans nothing moved.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].status","typeLabel":"enum\u003cstring>"},"requested_via":{"type":"string","enum":["api","console","automatic","external"],"description":"Which lane issued it. `api` is your own\n`POST /v1/deliveries/{id}/refunds`. `console` is the merchant\npressing it in the Listo console. `automatic` is one we issued\nwith nobody asking, on a cancel or a return. `external` is one\nmade in the merchant's own payment dashboard, which we learned\nabout afterwards.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].requested_via","typeLabel":"enum\u003cstring>"},"party":{"type":"string","enum":["business","platform"],"description":"Whose decision it was. `business` is the merchant, or you\nacting for your store. `platform` is Listo issuing it because\nthe order ended without reaching the customer.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].party","typeLabel":"enum\u003cstring>"},"created_at":{"type":"string","format":"date-time","description":"When the refund was asked for.","isRequired":true,"uniqueKey":"refunds.items.[INDEX].created_at","typeLabel":"string\u003cdate-time>"},"succeeded_at":{"oneOf":[{"type":"string","format":"date-time","description":"When the money was confirmed on its way. Null on a pending or\nfailed refund.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].succeeded_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When the money was confirmed on its way. Null on a pending or\nfailed refund.\n","isRequired":true,"uniqueKey":"refunds.items.[INDEX].succeeded_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"refunds.items.[INDEX].succeeded_at","typeLabel":"string\u003cdate-time> | null"}},"required":["id","amount_cents","reason","status","requested_via","party","created_at","succeeded_at"],"uniqueKey":"refunds.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"refunds","typeLabel":"object[]"},"refunded_cents":{"type":"integer","description":"The sum of the SUCCEEDED refunds above, in Mexican peso cents. 0,\nnever null, on an order nothing has gone back on. A `pending` refund\nis not counted, because the money has not moved and may yet be\nrefused.\n\nPublished rather than left for you to compute, because the array\ncannot always be summed correctly: a refund of the remaining balance\ncarries no `amount_cents` until it is confirmed, so adding the\nlist up counts it as zero and reads the whole charge as still\nrefundable. We take that case pessimistically instead, as having\ntaken the entire remaining balance, because guessing low ends in\nrefunding a customer twice.\n","isRequired":true,"uniqueKey":"refunded_cents","typeLabel":"integer"},"refundable_cents":{"type":"integer","description":"What a further refund could still return: what the customer paid\n(`order_value_cents` plus `customer_fee_cents`, which IS the charge)\nminus `refunded_cents`, floored at zero. 0, never null.\n\n0 on any order with no collected card payment behind it, whatever\nits value. Cash never touched our rails, a prepaid order's customer\nis yours and we hold nothing of theirs, and an order still awaiting\npayment was never charged, so there is nothing on any of them a\nrefund could return.\n\nThis is the ceiling `POST /v1/deliveries/{id}/refunds` refuses an\n`amount_cents` against, so read it before you call rather than\ndiscovering the bound from a `refund_amount_too_large`.\n","isRequired":true,"uniqueKey":"refundable_cents","typeLabel":"integer"},"support_reference":{"oneOf":[{"type":"string","description":"A short handle to quote when you escalate an order to us. Null until\nthe order reaches dispatch.\n","example":"2080F","isRequired":true,"uniqueKey":"support_reference.oneOf.0","typeLabel":"string"},{"type":"null","description":"A short handle to quote when you escalate an order to us. Null until\nthe order reaches dispatch.\n","example":"2080F","isRequired":true,"uniqueKey":"support_reference.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"support_reference","typeLabel":"string | null"},"pickup_ready_at":{"oneOf":[{"type":"string","format":"date-time","description":"The scheduled pickup time you asked for. Null on a dispatch-now order.","isRequired":true,"uniqueKey":"pickup_ready_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"The scheduled pickup time you asked for. Null on a dispatch-now order.","isRequired":true,"uniqueKey":"pickup_ready_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"pickup_ready_at","typeLabel":"string\u003cdate-time> | null"},"requested_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"requested_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"requested_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"requested_at","typeLabel":"string\u003cdate-time> | null"},"courier_assigned_at":{"oneOf":[{"type":"string","format":"date-time","description":"When a courier was first observed on the current attempt. Honestly\nan observation rather than an assignment clock, because no\nassignment time is published to us. Null on an attempt that never\nhad one.\n","isRequired":true,"uniqueKey":"courier_assigned_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","description":"When a courier was first observed on the current attempt. Honestly\nan observation rather than an assignment clock, because no\nassignment time is published to us. Null on an attempt that never\nhad one.\n","isRequired":true,"uniqueKey":"courier_assigned_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"courier_assigned_at","typeLabel":"string\u003cdate-time> | null"},"picked_up_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"picked_up_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"picked_up_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"picked_up_at","typeLabel":"string\u003cdate-time> | null"},"delivered_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"delivered_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"delivered_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"delivered_at","typeLabel":"string\u003cdate-time> | null"},"canceled_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"canceled_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"canceled_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"canceled_at","typeLabel":"string\u003cdate-time> | null"},"returned_at":{"oneOf":[{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"returned_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","isRequired":true,"uniqueKey":"returned_at.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"returned_at","typeLabel":"string\u003cdate-time> | null"},"created_at":{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"created_at","typeLabel":"string\u003cdate-time>"},"updated_at":{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"updated_at","typeLabel":"string\u003cdate-time>"}},"required":["id","external_id","origin","code","status","store_id","test","payment","order_value_cents","fee_cents","business_fee_cents","customer_fee_cents","tip_cents","currency","quote_id","pickup","dropoff","manifest","vehicle","courier","pickup_eta_at","dropoff_eta_at","tracking_url","verification","return_verification","proof","proofs","cancellation","failure_reason","support_reference","pickup_ready_at","requested_at","courier_assigned_at","picked_up_at","delivered_at","canceled_at","returned_at","created_at","updated_at","charges","refunds","refunded_cents","refundable_cents"],"uniqueKey":"","typeLabel":"object"},"example":{"id":"dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe","external_id":"order-841","code":"LS-2001","status":"dispatching","store_id":"st_4bR8kM2wQ7xT1nV5cZ9pLdYe","test":true,"payment":{"method":"prepaid","status":"paid","url":null,"cash_to_collect_cents":null,"paid_at":"2026-07-29T09:16:40Z"},"order_value_cents":19600,"fee_cents":7424,"business_fee_cents":7424,"customer_fee_cents":0,"currency":"MXN","quote_id":null,"pickup":{"name":"Tortería La Mera Mera","street":"Av. Juan Palomar y Arias 765, Zapopan, Jalisco 45110","lat":20.691145,"lng":-103.426885,"notes":null},"dropoff":{"name":"Ana Torres","phone":"+528112345678","street":"Blvd. Puerta de Hierro 4965, Zapopan, Jalisco 45116","apt_unit":null,"lat":20.709395,"lng":-103.413393,"notes":null,"handoff":"meet_at_door"},"manifest":null,"courier":null,"pickup_eta_at":"2026-07-29T09:24:30Z","dropoff_eta_at":"2026-07-29T09:50:38Z","tracking_url":"https://app.listo.delivery/t/abc123","verification":null,"proof":null,"cancellation":null,"failure_reason":null,"support_reference":"2080F","pickup_ready_at":null,"requested_at":"2026-07-29T09:16:40Z","courier_assigned_at":null,"picked_up_at":null,"delivered_at":null,"canceled_at":null,"returned_at":null,"created_at":"2026-07-29T09:16:40Z","updated_at":"2026-07-29T09:16:40Z","charges":[],"tip_cents":0,"origin":"api","refunds":[],"refunded_cents":0,"refundable_cents":0,"vehicle":"moto","return_verification":{"pincode":null},"proofs":{"pickup":null,"dropoff":null,"return":null}},"examples":{"Example":{"title":"Example","value":{"id":"dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe","external_id":"order-841","code":"LS-2001","status":"dispatching","store_id":"st_4bR8kM2wQ7xT1nV5cZ9pLdYe","test":true,"payment":{"method":"prepaid","status":"paid","url":null,"cash_to_collect_cents":null,"paid_at":"2026-07-29T09:16:40Z"},"order_value_cents":19600,"fee_cents":7424,"business_fee_cents":7424,"customer_fee_cents":0,"currency":"MXN","quote_id":null,"pickup":{"name":"Tortería La Mera Mera","street":"Av. Juan Palomar y Arias 765, Zapopan, Jalisco 45110","lat":20.691145,"lng":-103.426885,"notes":null},"dropoff":{"name":"Ana Torres","phone":"+528112345678","street":"Blvd. Puerta de Hierro 4965, Zapopan, Jalisco 45116","apt_unit":null,"lat":20.709395,"lng":-103.413393,"notes":null,"handoff":"meet_at_door"},"manifest":null,"courier":null,"pickup_eta_at":"2026-07-29T09:24:30Z","dropoff_eta_at":"2026-07-29T09:50:38Z","tracking_url":"https://app.listo.delivery/t/abc123","verification":null,"proof":null,"cancellation":null,"failure_reason":null,"support_reference":"2080F","pickup_ready_at":null,"requested_at":"2026-07-29T09:16:40Z","courier_assigned_at":null,"picked_up_at":null,"delivered_at":null,"canceled_at":null,"returned_at":null,"created_at":"2026-07-29T09:16:40Z","updated_at":"2026-07-29T09:16:40Z","charges":[],"tip_cents":0,"origin":"api","refunds":[],"refunded_cents":0,"refundable_cents":0,"vehicle":"moto","return_verification":{"pincode":null},"proofs":{"pickup":null,"dropoff":null,"return":null}}}},"exampleType":"examples"}},"exampleType":"examples"},"731d1c58-f612-4602-8432-ad96a94167c0":{"description":"A short-lived handle you sent is no longer good: a quote that expired or was already spent (`quote_expired`, `quote_consumed`), which you quote again, or a connection code past its window (`code_expired`), whose store was already connected when the owner approved and is on `GET /v1/stores`.","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"quote_consumed","message":"That quote was already used for a delivery. Request a new quote.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/quote_consumed","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"quote_consumed","message":"That quote was already used for a delivery. Request a new quote.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/quote_consumed","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"238c7b46-d2a4-48d5-afda-368712d99696":{"description":"The API key is missing, revoked, or not valid for this mode (`invalid_api_key`).","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"invalid_api_key","message":"The API key is missing, revoked, or not valid for this mode.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/invalid_api_key","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"invalid_api_key","message":"The API key is missing, revoked, or not valid for this mode.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/invalid_api_key","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"0011abc1-68e3-4749-b36c-042cf37073d3":{"description":"The prepaid balance does not cover this request (`insufficient_balance`).","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"examples":{"plan_required":{"title":"plan_required","value":{"error":{"code":"plan_required","message":"This store has no active plan.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/plan_required","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}},"insufficient_balance":{"title":"insufficient_balance","value":{"error":{"code":"insufficient_balance","message":"The account balance does not cover this request.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/insufficient_balance","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"def0f860-b288-4b6a-bf70-1566887dc9a1":{"description":"The key is valid and is read-only (`read_only_key`), so it can read but not create, change or cancel anything. Use a key issued without the read-only setting.","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"examples":{"read_only_key":{"title":"read_only_key","value":{"error":{"code":"read_only_key","message":"This API key is read-only. Use a key without the read-only setting to create, change or cancel anything.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/read_only_key","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"2dc0cdcd-2a89-49b4-b349-f8e657aa2665":{"description":"No object with that id exists for this key (`not_found`). Another account's objects are indistinguishable from ones that never existed.","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"not_found","message":"No resource with that id exists for this API key.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/not_found","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"not_found","message":"No resource with that id exists for this API key.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/not_found","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"be4dca18-ebdf-43cb-ad6c-fd8dd0ba0e99":{"description":"The request conflicts with the state of the world. The object is no longer in a state that allows this (`not_cancellable`, `not_reschedulable`, `not_retriable`, `not_editable`, `not_refundable`, `not_reactivatable`, `plan_ended`), the cancellation did not go through (`cancel_failed`), the plan's charge is still with the bank (`charge_in_progress`), the idempotency key was reused with a different body (`idempotency_conflict`, which no retry fixes), the idempotency key is still held by the request that first sent it (`idempotency_conflict`, and once that request has answered the same call with the same key replays its answer), there is no webhook URL configured for the key's mode so there is nowhere to send (`webhook_endpoint_not_configured`), the event is a courier position, which is never sent again (`event_not_resendable`), the email already has a Listo account (`account_exists`), the store's pickup point is still being moved (`pickup_point_pending`), or no courier is free for this trip right now (`no_couriers_available`). Worth repeating unchanged after a few minutes: `cancel_failed`, `charge_in_progress`, `pickup_point_pending`, `no_couriers_available`, and a call refused because its idempotency key is still held (repeat it with the same key). Every other refusal here needs the request or the object to change first.","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"idempotency_conflict","message":"That idempotency key was already used with a different body.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/idempotency_conflict","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"idempotency_conflict","message":"That idempotency key was already used with a different body.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/idempotency_conflict","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"12e83613-84ad-4024-9745-8494f63a7a55":{"description":"The request was understood and refused. `error.code` says why and `error.param` names the offending field when one applies.","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"address_undeliverable","message":"The dropoff address was rejected as undeliverable.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/address_undeliverable","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"address_undeliverable","message":"The dropoff address was rejected as undeliverable.","param":null,"retryable":false,"doc_url":"https://docs.listo.delivery/en/errors/address_undeliverable","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"},"568dc3b2-1a5d-4c89-b582-ac076dcc320e":{"description":"Dispatch is temporarily unavailable (`provider_unavailable`). Retry shortly.","content":{"application/json":{"schema":{"type":"object","description":"The body of every 4xx and 5xx answer on this API.","properties":{"error":{"type":"object","description":"What every refusal and every failure looks like. `code` is the only\nfield to branch on; `message` is for a human reading a log and may\nchange wording or language without notice.\n","properties":{"code":{"type":"string","description":"A stable, machine-readable name for what went wrong, such as\n`validation_failed`, `not_found` or `insufficient_balance`. Branch\non this and never on `message`. The set grows additively: handle a\ncode you do not know as the generic failure its status code says it\nis. Every code has a page under `doc_url`.\n","example":"validation_failed","isRequired":true,"uniqueKey":"error.code","typeLabel":"string"},"message":{"type":"string","description":"A sentence for the person debugging. Free to change wording or\nlanguage, so never match on it.\n","example":"order_value_cents is required.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"param":{"oneOf":[{"type":"string","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.0","typeLabel":"string"},{"type":"null","description":"The request field the refusal is about, when one applies, spelled\nas you sent it (`dropoff.street`, `manifest.items`). Null when the\nfailure is not about one field.\n","example":"order_value_cents","isRequired":true,"uniqueKey":"error.param.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"error.param","typeLabel":"string | null"},"retryable":{"type":"boolean","description":"Whether sending the identical request again could succeed. `false`\nmeans it never will without a change on your side or in the world.\n","example":false,"isRequired":true,"uniqueKey":"error.retryable","typeLabel":"boolean"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this `code`.","example":"https://docs.listo.delivery/en/errors/validation_failed","isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"request_id":{"type":"string","description":"Our id for this request, the same value as the `X-Request-Id`\nresponse header. Quote it to support, and filter\n`GET /v1/request_logs` by it.\n","example":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10","isRequired":true,"uniqueKey":"error.request_id","typeLabel":"string"}},"required":["code","message","param","retryable","doc_url","request_id"],"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"required":["error"],"uniqueKey":"","typeLabel":"object"},"example":{"error":{"code":"provider_unavailable","message":"Dispatch is temporarily unavailable. Nothing was created. Retry.","param":null,"retryable":true,"doc_url":"https://docs.listo.delivery/en/errors/provider_unavailable","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}},"examples":{"Example":{"title":"Example","value":{"error":{"code":"provider_unavailable","message":"Dispatch is temporarily unavailable. Nothing was created. Retry.","param":null,"retryable":true,"doc_url":"https://docs.listo.delivery/en/errors/provider_unavailable","request_id":"0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"}}}},"exampleType":"examples"}},"exampleType":"examples"}},"security":{"6f36371e-99e1-485d-8391-2569b8d80504":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Your API key, sent as `Authorization: Bearer \u003ckey>`. Live and test keys are separate; the key you send decides which world the call acts in. To try calls, use a test key (it starts with `lst_test_`): no real courier is sent and no real money moves, but webhooks fire and the `dropoff.phone` you give can receive WhatsApp messages, so use your own number. A live key (it starts with `lst_live_`) books and charges real deliveries.","scopes":[],"schemeName":"BearerAuth"}}},"servers":{"a658606d-cd78-44bb-b1d3-622847e198a1":{"url":"https://api.listo.delivery"}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","description":"Your API key, sent as `Authorization: Bearer \u003ckey>`. Live and test keys are separate; the key you send decides which world the call acts in. To try calls, use a test key (it starts with `lst_test_`): no real courier is sent and no real money moves, but webhooks fire and the `dropoff.phone` you give can receive WhatsApp messages, so use your own number. A live key (it starts with `lst_live_`) books and charges real deliveries.","scopes":[],"schemeName":"BearerAuth"},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Your API key, sent as `Authorization: Bearer \u003ckey>`. Live and test keys are separate; the key you send decides which world the call acts in. To try calls, use a test key (it starts with `lst_test_`): no real courier is sent and no real money moves, but webhooks fire and the `dropoff.phone` you give can receive WhatsApp messages, so use your own number. A live key (it starts with `lst_live_`) books and charges real deliveries.","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"BearerAuth","name":"Authorization"}],"title":"BearerAuth"}]},"originalFileLocation":"openapi/en.yaml"},"subdomain":"listo-c5a11a00","siteName":"Listo Partner API","title":"Create a delivery - Listo Partner API","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{"en":{"href":"/api-reference/deliveries/create-a-delivery","matchLevel":0},"es":{"href":"/es/api-reference/deliveries/create-a-delivery","matchLevel":1}}},"lastModified":"2026-11-07T01:48:55.114Z","originalFileLocation":"api-reference/deliveries/create-a-delivery.mdx","locale":"en"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Dispatches a delivery. Idempotency-Key is required. Send a value you generate per order, and retry the same call with the same key if you never saw a response: a replay within 24 hours returns the original response, status code included, without dispatching a second courier or debiting a second time. The same key with a different body is a 409, and so is a repeat sent while the original request is still being processed (idempotency_conflict both times). That repeat created nothing and does not mean the original failed: once the original has answered, the same request with the same key replays its answer. dropoff.phone is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with validation_failed. external_id is your own order reference. It is unique per key world, echoed in every payload and webhook, and filterable on the list, so you can reconcile without ever storing our ids. pickup_ready_at schedules the pickup for a future moment, between 10 minutes and 29 days out. Absent, the order dispatches now. How the customer pays is payment_method. cash has the courier collect at the door, up to a cap. card mints a branded payment link in payment.url, which Listo sends your customer itself by WhatsApp (and by SMS when the WhatsApp does not reach them), so you do not need to send it; the order holds at awaiting_payment until the customer pays. prepaid means you have already been paid and the order dispatches immediately. On a prepaid create that binds no quote, business_fee_share is forced to 100 and business_fee_cap_cents is ignored, whatever you send: you have already collected from your customer, so the whole fee is yours. A create that binds a quote is held to the split the quote was priced with, so request the quote with payment_method: prepaid or with no payment_method: either is always priced at share 100 and binds a prepaid create. Any other quote is refused with 422 validation_failed, and the first thing that does not match is the one named. A quote requested with another payment_method (cash or card) is refused naming payment_method. A quote whose split is not the one a prepaid order has (share 100, no ceiling) is refused naming business_fee_share or business_fee_cap_cents, and error.message says why and what to send instead: a new quote requested with payment_method: prepaid. vehicle is moto (the default) or car, and manifest.items[].size is small (the default), medium, large or xlarge. A trip goes by car when you ask for one or when any item is sized xlarge, and for no other reason. A car is a sedan, never a van, can take longer to assign, and adds a surcharge inside fee_cents. The delivery\u0026#x27;s vehicle says what the trip was priced and dispatched as; courier.vehicle_type is what actually arrived, which can be a car on a moto trip without the fee changing. A create that binds a quote is held to the vehicle the quote was priced as. Leave vehicle out and it inherits the quote\u0026#x27;s. Say moto against a car quote, car against a moto quote, or send an item sized xlarge against a moto quote, and it is refused with 422 validation_failed naming vehicle (or manifest.items when the sizes are what disagree). Re-quote with the cart you mean to send. simulate rehearses what goes wrong, and it works on a test key only. A live key that sends it is refused with 422 validation_failed naming simulate, and so is a value that is not one of the three below. Leave it out and the delivery plays the normal happy path. - returned: the courier reaches the door, cannot hand the order over and brings it back. The delivery goes to returning and ends at returned with failure_reason: customer_unavailable, and the return is billed as a live one is: one return_fee entry in charges covers both legs (see charges). - no_courier: the create answers the same 409 no_couriers_available a live create answers when every courier is busy, and nothing is created, charged or recorded under your Idempotency-Key. There is no delivery to retry; repeat the call. - delayed_pickup: the courier is late. Pickup happens about 25 minutes after the create and the drop-off about 5 minutes after that, counted from pickup_ready_at instead when you sent one (the delivery waits in scheduled until then). It goes through the same statuses as the normal run. In test mode a delivery may go from dispatching straight to picking_up without ever showing a courier_assigned status, in every scenario. Do not wait for it: courier_assigned_at and the courier object are filled in. There is no scenario for a courier who gives up before pickup. The same Idempotency-Key with a different simulate is a 409 idempotency_conflict, like any other changed body, once a request has used the key (it created a delivery, or it is still being processed). A request refused before it created anything, such as the no_courier 409, leaves the key free: send it again with a different simulate, or none, and it is a new request. A returned delivery cannot be retried (409 not_retriable): its order ended. A cart too large for one courier trip is refused with 422 order_too_large, naming manifest.items, and nothing is dispatched or debited. Split it across two deliveries. Listo delivers in Mexico. A store that turns out to be somewhere else is refused with 422 unsupported_country and nothing is dispatched or debited; it is final rather than retryable, and the same store will be refused every time. A store whose pickup point we cannot currently vouch for is refused with 409 pickup_point_pending, and nothing is dispatched or debited: until it is confirmed a courier could be sent to the wrong place. Usually the store\u0026#x27;s address has just changed and the confirmation has not landed yet, but not always, so do not read it as \u0026quot;the merchant moved\u0026quot;. Unlike the refusal above it this one IS retryable, because nothing about your request is wrong and the identical call succeeds once the pickup point is confirmed. Retry it on a minutes-scale backoff, for a bounded number of attempts, and then put it in front of a human rather than keep polling. Most moves land within minutes on their own; a few are held up by something only our staff can clear and take considerably longer, and this response does not tell the two apart. There is no Retry-After on it for that reason: we would be inventing the number. The restaurant can also unblock it themselves from their own Listo console, which is usually the faster of the two. When every courier near the trip is busy as the create prices it, the create is refused with 409 no_couriers_available, and nothing is dispatched or debited. It is retryable: repeat the identical request, with the same Idempotency-Key, after a few minutes, for a bounded number of attempts. Nothing was recorded under that key, so the repeat creates the order once a courier is free rather than replaying the refusal. A live store whose Plan Listo is not paid up is refused with 402 plan_required, and nothing is dispatched or debited. It is a different refusal from 402 insufficient_balance, which is about your balance: read error.code, never the status alone. error.message names the reason (no active plan, a charge that is pending, a plan that ended on a given date, a plan whose included deliveries for the cycle are all used, or a store that cannot request couriers right now), and it is not retryable. See Plan and billing. A dropoff beyond the store\u0026#x27;s 15 km service area is refused with 422 out_of_service_area, which is final. The courier network can also refuse the distance of a dropoff at the very edge of that circle, and the answer is the same 422, whether it refuses on the price or on the create itself. On the create it comes after the delivery was recorded, so the delivery stays, ended at no_courier with failure_reason: delivery_failed under your external_id, its fee debit returned in full and nothing else charged; replaying the request answers the same 422. Send that order to another address under a new external_id.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Listo Partner API\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Create a delivery - Listo Partner API\">\u003cmeta data-mint-head property=\"og:description\" content=\"Dispatches a delivery. Idempotency-Key is required. Send a value you generate per order, and retry the same call with the same key if you never saw a response: a replay within 24 hours returns the original response, status code included, without dispatching a second courier or debiting a second time. The same key with a different body is a 409, and so is a repeat sent while the original request is still being processed (idempotency_conflict both times). That repeat created nothing and does not mean the original failed: once the original has answered, the same request with the same key replays its answer. dropoff.phone is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with validation_failed. external_id is your own order reference. It is unique per key world, echoed in every payload and webhook, and filterable on the list, so you can reconcile without ever storing our ids. pickup_ready_at schedules the pickup for a future moment, between 10 minutes and 29 days out. Absent, the order dispatches now. How the customer pays is payment_method. cash has the courier collect at the door, up to a cap. card mints a branded payment link in payment.url, which Listo sends your customer itself by WhatsApp (and by SMS when the WhatsApp does not reach them), so you do not need to send it; the order holds at awaiting_payment until the customer pays. prepaid means you have already been paid and the order dispatches immediately. On a prepaid create that binds no quote, business_fee_share is forced to 100 and business_fee_cap_cents is ignored, whatever you send: you have already collected from your customer, so the whole fee is yours. A create that binds a quote is held to the split the quote was priced with, so request the quote with payment_method: prepaid or with no payment_method: either is always priced at share 100 and binds a prepaid create. Any other quote is refused with 422 validation_failed, and the first thing that does not match is the one named. A quote requested with another payment_method (cash or card) is refused naming payment_method. A quote whose split is not the one a prepaid order has (share 100, no ceiling) is refused naming business_fee_share or business_fee_cap_cents, and error.message says why and what to send instead: a new quote requested with payment_method: prepaid. vehicle is moto (the default) or car, and manifest.items[].size is small (the default), medium, large or xlarge. A trip goes by car when you ask for one or when any item is sized xlarge, and for no other reason. A car is a sedan, never a van, can take longer to assign, and adds a surcharge inside fee_cents. The delivery\u0026#x27;s vehicle says what the trip was priced and dispatched as; courier.vehicle_type is what actually arrived, which can be a car on a moto trip without the fee changing. A create that binds a quote is held to the vehicle the quote was priced as. Leave vehicle out and it inherits the quote\u0026#x27;s. Say moto against a car quote, car against a moto quote, or send an item sized xlarge against a moto quote, and it is refused with 422 validation_failed naming vehicle (or manifest.items when the sizes are what disagree). Re-quote with the cart you mean to send. simulate rehearses what goes wrong, and it works on a test key only. A live key that sends it is refused with 422 validation_failed naming simulate, and so is a value that is not one of the three below. Leave it out and the delivery plays the normal happy path. - returned: the courier reaches the door, cannot hand the order over and brings it back. The delivery goes to returning and ends at returned with failure_reason: customer_unavailable, and the return is billed as a live one is: one return_fee entry in charges covers both legs (see charges). - no_courier: the create answers the same 409 no_couriers_available a live create answers when every courier is busy, and nothing is created, charged or recorded under your Idempotency-Key. There is no delivery to retry; repeat the call. - delayed_pickup: the courier is late. Pickup happens about 25 minutes after the create and the drop-off about 5 minutes after that, counted from pickup_ready_at instead when you sent one (the delivery waits in scheduled until then). It goes through the same statuses as the normal run. In test mode a delivery may go from dispatching straight to picking_up without ever showing a courier_assigned status, in every scenario. Do not wait for it: courier_assigned_at and the courier object are filled in. There is no scenario for a courier who gives up before pickup. The same Idempotency-Key with a different simulate is a 409 idempotency_conflict, like any other changed body, once a request has used the key (it created a delivery, or it is still being processed). A request refused before it created anything, such as the no_courier 409, leaves the key free: send it again with a different simulate, or none, and it is a new request. A returned delivery cannot be retried (409 not_retriable): its order ended. A cart too large for one courier trip is refused with 422 order_too_large, naming manifest.items, and nothing is dispatched or debited. Split it across two deliveries. Listo delivers in Mexico. A store that turns out to be somewhere else is refused with 422 unsupported_country and nothing is dispatched or debited; it is final rather than retryable, and the same store will be refused every time. A store whose pickup point we cannot currently vouch for is refused with 409 pickup_point_pending, and nothing is dispatched or debited: until it is confirmed a courier could be sent to the wrong place. Usually the store\u0026#x27;s address has just changed and the confirmation has not landed yet, but not always, so do not read it as \u0026quot;the merchant moved\u0026quot;. Unlike the refusal above it this one IS retryable, because nothing about your request is wrong and the identical call succeeds once the pickup point is confirmed. Retry it on a minutes-scale backoff, for a bounded number of attempts, and then put it in front of a human rather than keep polling. Most moves land within minutes on their own; a few are held up by something only our staff can clear and take considerably longer, and this response does not tell the two apart. There is no Retry-After on it for that reason: we would be inventing the number. The restaurant can also unblock it themselves from their own Listo console, which is usually the faster of the two. When every courier near the trip is busy as the create prices it, the create is refused with 409 no_couriers_available, and nothing is dispatched or debited. It is retryable: repeat the identical request, with the same Idempotency-Key, after a few minutes, for a bounded number of attempts. Nothing was recorded under that key, so the repeat creates the order once a courier is free rather than replaying the refusal. A live store whose Plan Listo is not paid up is refused with 402 plan_required, and nothing is dispatched or debited. It is a different refusal from 402 insufficient_balance, which is about your balance: read error.code, never the status alone. error.message names the reason (no active plan, a charge that is pending, a plan that ended on a given date, a plan whose included deliveries for the cycle are all used, or a store that cannot request couriers right now), and it is not retryable. See Plan and billing. A dropoff beyond the store\u0026#x27;s 15 km service area is refused with 422 out_of_service_area, which is final. The courier network can also refuse the distance of a dropoff at the very edge of that circle, and the answer is the same 422, whether it refuses on the price or on the create itself. On the create it comes after the delivery was recorded, so the delivery stays, ended at no_courier with failure_reason: delivery_failed under your external_id, its fee debit returned in full and nothing else charged; replaying the request answers the same 422. Send that order to another address under a new external_id.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Listo Partner API\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://listo-c5a11a00.mintlify.site/_mintlify/api/og/v2?title=Create+a+delivery\u0026amp;description=Dispatches+a+delivery.%0A%0A%60Idempotency-Key%60+is+required.+Send+a+value+you+generate+per+order%2C+and%0Aretry+the+same+call+with+the+same+key+if+you+never+saw+a+respons\u0026amp;theme=77fcfc9066d8b9eb9850b729\u0026amp;signature=b455dfeda6fbb71e016dca59323dd66b8b2b8fed65390fd278c1aa7da37da7c1\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Create a delivery - Listo Partner API\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Dispatches a delivery. Idempotency-Key is required. Send a value you generate per order, and retry the same call with the same key if you never saw a response: a replay within 24 hours returns the original response, status code included, without dispatching a second courier or debiting a second time. The same key with a different body is a 409, and so is a repeat sent while the original request is still being processed (idempotency_conflict both times). That repeat created nothing and does not mean the original failed: once the original has answered, the same request with the same key replays its answer. dropoff.phone is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with validation_failed. external_id is your own order reference. It is unique per key world, echoed in every payload and webhook, and filterable on the list, so you can reconcile without ever storing our ids. pickup_ready_at schedules the pickup for a future moment, between 10 minutes and 29 days out. Absent, the order dispatches now. How the customer pays is payment_method. cash has the courier collect at the door, up to a cap. card mints a branded payment link in payment.url, which Listo sends your customer itself by WhatsApp (and by SMS when the WhatsApp does not reach them), so you do not need to send it; the order holds at awaiting_payment until the customer pays. prepaid means you have already been paid and the order dispatches immediately. On a prepaid create that binds no quote, business_fee_share is forced to 100 and business_fee_cap_cents is ignored, whatever you send: you have already collected from your customer, so the whole fee is yours. A create that binds a quote is held to the split the quote was priced with, so request the quote with payment_method: prepaid or with no payment_method: either is always priced at share 100 and binds a prepaid create. Any other quote is refused with 422 validation_failed, and the first thing that does not match is the one named. A quote requested with another payment_method (cash or card) is refused naming payment_method. A quote whose split is not the one a prepaid order has (share 100, no ceiling) is refused naming business_fee_share or business_fee_cap_cents, and error.message says why and what to send instead: a new quote requested with payment_method: prepaid. vehicle is moto (the default) or car, and manifest.items[].size is small (the default), medium, large or xlarge. A trip goes by car when you ask for one or when any item is sized xlarge, and for no other reason. A car is a sedan, never a van, can take longer to assign, and adds a surcharge inside fee_cents. The delivery\u0026#x27;s vehicle says what the trip was priced and dispatched as; courier.vehicle_type is what actually arrived, which can be a car on a moto trip without the fee changing. A create that binds a quote is held to the vehicle the quote was priced as. Leave vehicle out and it inherits the quote\u0026#x27;s. Say moto against a car quote, car against a moto quote, or send an item sized xlarge against a moto quote, and it is refused with 422 validation_failed naming vehicle (or manifest.items when the sizes are what disagree). Re-quote with the cart you mean to send. simulate rehearses what goes wrong, and it works on a test key only. A live key that sends it is refused with 422 validation_failed naming simulate, and so is a value that is not one of the three below. Leave it out and the delivery plays the normal happy path. - returned: the courier reaches the door, cannot hand the order over and brings it back. The delivery goes to returning and ends at returned with failure_reason: customer_unavailable, and the return is billed as a live one is: one return_fee entry in charges covers both legs (see charges). - no_courier: the create answers the same 409 no_couriers_available a live create answers when every courier is busy, and nothing is created, charged or recorded under your Idempotency-Key. There is no delivery to retry; repeat the call. - delayed_pickup: the courier is late. Pickup happens about 25 minutes after the create and the drop-off about 5 minutes after that, counted from pickup_ready_at instead when you sent one (the delivery waits in scheduled until then). It goes through the same statuses as the normal run. In test mode a delivery may go from dispatching straight to picking_up without ever showing a courier_assigned status, in every scenario. Do not wait for it: courier_assigned_at and the courier object are filled in. There is no scenario for a courier who gives up before pickup. The same Idempotency-Key with a different simulate is a 409 idempotency_conflict, like any other changed body, once a request has used the key (it created a delivery, or it is still being processed). A request refused before it created anything, such as the no_courier 409, leaves the key free: send it again with a different simulate, or none, and it is a new request. A returned delivery cannot be retried (409 not_retriable): its order ended. A cart too large for one courier trip is refused with 422 order_too_large, naming manifest.items, and nothing is dispatched or debited. Split it across two deliveries. Listo delivers in Mexico. A store that turns out to be somewhere else is refused with 422 unsupported_country and nothing is dispatched or debited; it is final rather than retryable, and the same store will be refused every time. A store whose pickup point we cannot currently vouch for is refused with 409 pickup_point_pending, and nothing is dispatched or debited: until it is confirmed a courier could be sent to the wrong place. Usually the store\u0026#x27;s address has just changed and the confirmation has not landed yet, but not always, so do not read it as \u0026quot;the merchant moved\u0026quot;. Unlike the refusal above it this one IS retryable, because nothing about your request is wrong and the identical call succeeds once the pickup point is confirmed. Retry it on a minutes-scale backoff, for a bounded number of attempts, and then put it in front of a human rather than keep polling. Most moves land within minutes on their own; a few are held up by something only our staff can clear and take considerably longer, and this response does not tell the two apart. There is no Retry-After on it for that reason: we would be inventing the number. The restaurant can also unblock it themselves from their own Listo console, which is usually the faster of the two. When every courier near the trip is busy as the create prices it, the create is refused with 409 no_couriers_available, and nothing is dispatched or debited. It is retryable: repeat the identical request, with the same Idempotency-Key, after a few minutes, for a bounded number of attempts. Nothing was recorded under that key, so the repeat creates the order once a courier is free rather than replaying the refusal. A live store whose Plan Listo is not paid up is refused with 402 plan_required, and nothing is dispatched or debited. It is a different refusal from 402 insufficient_balance, which is about your balance: read error.code, never the status alone. error.message names the reason (no active plan, a charge that is pending, a plan that ended on a given date, a plan whose included deliveries for the cycle are all used, or a store that cannot request couriers right now), and it is not retryable. See Plan and billing. A dropoff beyond the store\u0026#x27;s 15 km service area is refused with 422 out_of_service_area, which is final. The courier network can also refuse the distance of a dropoff at the very edge of that circle, and the answer is the same 422, whether it refuses on the price or on the create itself. On the create it comes after the delivery was recorded, so the delivery stays, ended at no_courier with failure_reason: delivery_failed under your external_id, its fee debit returned in full and nothing else charged; replaying the request answers the same 422. Send that order to another address under a new external_id.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://listo-c5a11a00.mintlify.site/_mintlify/api/og/v2?title=Create+a+delivery\u0026amp;description=Dispatches+a+delivery.%0A%0A%60Idempotency-Key%60+is+required.+Send+a+value+you+generate+per+order%2C+and%0Aretry+the+same+call+with+the+same+key+if+you+never+saw+a+respons\u0026amp;theme=77fcfc9066d8b9eb9850b729\u0026amp;signature=b455dfeda6fbb71e016dca59323dd66b8b2b8fed65390fd278c1aa7da37da7c1\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/listo-c5a11a00/GMBEN5uWEX-r6cRJ/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Listo Partner API\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#15171C\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/deliveries/create-a-delivery.md\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"en\" href=\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"es\" href=\"https://docs.listo.delivery/es/api-reference/deliveries/create-a-delivery\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"x-default\" href=\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.listo.delivery/#organization\",\"name\":\"Listo Partner API\",\"url\":\"https://docs.listo.delivery\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/listo-c5a11a00/mreCwXzcEA2bSZog/logo/light.svg?fit=max\u0026auto=format\u0026n=mreCwXzcEA2bSZog\u0026q=85\u0026s=da2b28d464dc6987329dfd5971085dd7\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.listo.delivery#website\",\"name\":\"Listo Partner API\",\"url\":\"https://docs.listo.delivery\",\"publisher\":{\"@id\":\"https://docs.listo.delivery/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery#webpage\",\"url\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\",\"name\":\"Create a delivery\",\"description\":\"Dispatches a delivery. Idempotency-Key is required. Send a value you generate per order, and retry the same call with the same key if you never saw a response: a replay within 24 hours returns the original response, status code included, without dispatching a second courier or debiting a second time. The same key with a different body is a 409, and so is a repeat sent while the original request is still being processed (idempotency_conflict both times). That repeat created nothing and does not mean the original failed: once the original has answered, the same request with the same key replays its answer. dropoff.phone is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with validation_failed. external_id is your own order reference. It is unique per key world, echoed in every payload and webhook, and filterable on the list, so you can reconcile without ever storing our ids. pickup_ready_at schedules the pickup for a future moment, between 10 minutes and 29 days out. Absent, the order dispatches now. How the customer pays is payment_method. cash has the courier collect at the door, up to a cap. card mints a branded payment link in payment.url, which Listo sends your customer itself by WhatsApp (and by SMS when the WhatsApp does not reach them), so you do not need to send it; the order holds at awaiting_payment until the customer pays. prepaid means you have already been paid and the order dispatches immediately. On a prepaid create that binds no quote, business_fee_share is forced to 100 and business_fee_cap_cents is ignored, whatever you send: you have already collected from your customer, so the whole fee is yours. A create that binds a quote is held to the split the quote was priced with, so request the quote with payment_method: prepaid or with no payment_method: either is always priced at share 100 and binds a prepaid create. Any other quote is refused with 422 validation_failed, and the first thing that does not match is the one named. A quote requested with another payment_method (cash or card) is refused naming payment_method. A quote whose split is not the one a prepaid order has (share 100, no ceiling) is refused naming business_fee_share or business_fee_cap_cents, and error.message says why and what to send instead: a new quote requested with payment_method: prepaid. vehicle is moto (the default) or car, and manifest.items[].size is small (the default), medium, large or xlarge. A trip goes by car when you ask for one or when any item is sized xlarge, and for no other reason. A car is a sedan, never a van, can take longer to assign, and adds a surcharge inside fee_cents. The delivery's vehicle says what the trip was priced and dispatched as; courier.vehicle_type is what actually arrived, which can be a car on a moto trip without the fee changing. A create that binds a quote is held to the vehicle the quote was priced as. Leave vehicle out and it inherits the quote's. Say moto against a car quote, car against a moto quote, or send an item sized xlarge against a moto quote, and it is refused with 422 validation_failed naming vehicle (or manifest.items when the sizes are what disagree). Re-quote with the cart you mean to send. simulate rehearses what goes wrong, and it works on a test key only. A live key that sends it is refused with 422 validation_failed naming simulate, and so is a value that is not one of the three below. Leave it out and the delivery plays the normal happy path. - returned: the courier reaches the door, cannot hand the order over and brings it back. The delivery goes to returning and ends at returned with failure_reason: customer_unavailable, and the return is billed as a live one is: one return_fee entry in charges covers both legs (see charges). - no_courier: the create answers the same 409 no_couriers_available a live create answers when every courier is busy, and nothing is created, charged or recorded under your Idempotency-Key. There is no delivery to retry; repeat the call. - delayed_pickup: the courier is late. Pickup happens about 25 minutes after the create and the drop-off about 5 minutes after that, counted from pickup_ready_at instead when you sent one (the delivery waits in scheduled until then). It goes through the same statuses as the normal run. In test mode a delivery may go from dispatching straight to picking_up without ever showing a courier_assigned status, in every scenario. Do not wait for it: courier_assigned_at and the courier object are filled in. There is no scenario for a courier who gives up before pickup. The same Idempotency-Key with a different simulate is a 409 idempotency_conflict, like any other changed body, once a request has used the key (it created a delivery, or it is still being processed). A request refused before it created anything, such as the no_courier 409, leaves the key free: send it again with a different simulate, or none, and it is a new request. A returned delivery cannot be retried (409 not_retriable): its order ended. A cart too large for one courier trip is refused with 422 order_too_large, naming manifest.items, and nothing is dispatched or debited. Split it across two deliveries. Listo delivers in Mexico. A store that turns out to be somewhere else is refused with 422 unsupported_country and nothing is dispatched or debited; it is final rather than retryable, and the same store will be refused every time. A store whose pickup point we cannot currently vouch for is refused with 409 pickup_point_pending, and nothing is dispatched or debited: until it is confirmed a courier could be sent to the wrong place. Usually the store's address has just changed and the confirmation has not landed yet, but not always, so do not read it as \\\"the merchant moved\\\". Unlike the refusal above it this one IS retryable, because nothing about your request is wrong and the identical call succeeds once the pickup point is confirmed. Retry it on a minutes-scale backoff, for a bounded number of attempts, and then put it in front of a human rather than keep polling. Most moves land within minutes on their own; a few are held up by something only our staff can clear and take considerably longer, and this response does not tell the two apart. There is no Retry-After on it for that reason: we would be inventing the number. The restaurant can also unblock it themselves from their own Listo console, which is usually the faster of the two. When every courier near the trip is busy as the create prices it, the create is refused with 409 no_couriers_available, and nothing is dispatched or debited. It is retryable: repeat the identical request, with the same Idempotency-Key, after a few minutes, for a bounded number of attempts. Nothing was recorded under that key, so the repeat creates the order once a courier is free rather than replaying the refusal. A live store whose Plan Listo is not paid up is refused with 402 plan_required, and nothing is dispatched or debited. It is a different refusal from 402 insufficient_balance, which is about your balance: read error.code, never the status alone. error.message names the reason (no active plan, a charge that is pending, a plan that ended on a given date, a plan whose included deliveries for the cycle are all used, or a store that cannot request couriers right now), and it is not retryable. See Plan and billing. A dropoff beyond the store's 15 km service area is refused with 422 out_of_service_area, which is final. The courier network can also refuse the distance of a dropoff at the very edge of that circle, and the answer is the same 422, whether it refuses on the price or on the create itself. On the create it comes after the delivery was recorded, so the delivery stays, ended at no_courier with failure_reason: delivery_failed under your external_id, its fee debit returned in full and nothing else charged; replaying the request answers the same 422. Send that order to another address under a new external_id.\",\"dateModified\":\"2026-11-07T01:48:55.114Z\",\"isPartOf\":{\"@id\":\"https://docs.listo.delivery#website\"},\"breadcrumb\":{\"@id\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Reference\",\"item\":\"https://docs.listo.delivery/api-reference/balance/read-your-prepaid-balance\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Deliveries\",\"item\":\"https://docs.listo.delivery/api-reference/deliveries/list-deliveries\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Create a delivery\",\"item\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery#apireference\",\"headline\":\"Create a delivery\",\"name\":\"Create a delivery\",\"description\":\"Dispatches a delivery. Idempotency-Key is required. Send a value you generate per order, and retry the same call with the same key if you never saw a response: a replay within 24 hours returns the original response, status code included, without dispatching a second courier or debiting a second time. The same key with a different body is a 409, and so is a repeat sent while the original request is still being processed (idempotency_conflict both times). That repeat created nothing and does not mean the original failed: once the original has answered, the same request with the same key replays its answer. dropoff.phone is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with validation_failed. external_id is your own order reference. It is unique per key world, echoed in every payload and webhook, and filterable on the list, so you can reconcile without ever storing our ids. pickup_ready_at schedules the pickup for a future moment, between 10 minutes and 29 days out. Absent, the order dispatches now. How the customer pays is payment_method. cash has the courier collect at the door, up to a cap. card mints a branded payment link in payment.url, which Listo sends your customer itself by WhatsApp (and by SMS when the WhatsApp does not reach them), so you do not need to send it; the order holds at awaiting_payment until the customer pays. prepaid means you have already been paid and the order dispatches immediately. On a prepaid create that binds no quote, business_fee_share is forced to 100 and business_fee_cap_cents is ignored, whatever you send: you have already collected from your customer, so the whole fee is yours. A create that binds a quote is held to the split the quote was priced with, so request the quote with payment_method: prepaid or with no payment_method: either is always priced at share 100 and binds a prepaid create. Any other quote is refused with 422 validation_failed, and the first thing that does not match is the one named. A quote requested with another payment_method (cash or card) is refused naming payment_method. A quote whose split is not the one a prepaid order has (share 100, no ceiling) is refused naming business_fee_share or business_fee_cap_cents, and error.message says why and what to send instead: a new quote requested with payment_method: prepaid. vehicle is moto (the default) or car, and manifest.items[].size is small (the default), medium, large or xlarge. A trip goes by car when you ask for one or when any item is sized xlarge, and for no other reason. A car is a sedan, never a van, can take longer to assign, and adds a surcharge inside fee_cents. The delivery's vehicle says what the trip was priced and dispatched as; courier.vehicle_type is what actually arrived, which can be a car on a moto trip without the fee changing. A create that binds a quote is held to the vehicle the quote was priced as. Leave vehicle out and it inherits the quote's. Say moto against a car quote, car against a moto quote, or send an item sized xlarge against a moto quote, and it is refused with 422 validation_failed naming vehicle (or manifest.items when the sizes are what disagree). Re-quote with the cart you mean to send. simulate rehearses what goes wrong, and it works on a test key only. A live key that sends it is refused with 422 validation_failed naming simulate, and so is a value that is not one of the three below. Leave it out and the delivery plays the normal happy path. - returned: the courier reaches the door, cannot hand the order over and brings it back. The delivery goes to returning and ends at returned with failure_reason: customer_unavailable, and the return is billed as a live one is: one return_fee entry in charges covers both legs (see charges). - no_courier: the create answers the same 409 no_couriers_available a live create answers when every courier is busy, and nothing is created, charged or recorded under your Idempotency-Key. There is no delivery to retry; repeat the call. - delayed_pickup: the courier is late. Pickup happens about 25 minutes after the create and the drop-off about 5 minutes after that, counted from pickup_ready_at instead when you sent one (the delivery waits in scheduled until then). It goes through the same statuses as the normal run. In test mode a delivery may go from dispatching straight to picking_up without ever showing a courier_assigned status, in every scenario. Do not wait for it: courier_assigned_at and the courier object are filled in. There is no scenario for a courier who gives up before pickup. The same Idempotency-Key with a different simulate is a 409 idempotency_conflict, like any other changed body, once a request has used the key (it created a delivery, or it is still being processed). A request refused before it created anything, such as the no_courier 409, leaves the key free: send it again with a different simulate, or none, and it is a new request. A returned delivery cannot be retried (409 not_retriable): its order ended. A cart too large for one courier trip is refused with 422 order_too_large, naming manifest.items, and nothing is dispatched or debited. Split it across two deliveries. Listo delivers in Mexico. A store that turns out to be somewhere else is refused with 422 unsupported_country and nothing is dispatched or debited; it is final rather than retryable, and the same store will be refused every time. A store whose pickup point we cannot currently vouch for is refused with 409 pickup_point_pending, and nothing is dispatched or debited: until it is confirmed a courier could be sent to the wrong place. Usually the store's address has just changed and the confirmation has not landed yet, but not always, so do not read it as \\\"the merchant moved\\\". Unlike the refusal above it this one IS retryable, because nothing about your request is wrong and the identical call succeeds once the pickup point is confirmed. Retry it on a minutes-scale backoff, for a bounded number of attempts, and then put it in front of a human rather than keep polling. Most moves land within minutes on their own; a few are held up by something only our staff can clear and take considerably longer, and this response does not tell the two apart. There is no Retry-After on it for that reason: we would be inventing the number. The restaurant can also unblock it themselves from their own Listo console, which is usually the faster of the two. When every courier near the trip is busy as the create prices it, the create is refused with 409 no_couriers_available, and nothing is dispatched or debited. It is retryable: repeat the identical request, with the same Idempotency-Key, after a few minutes, for a bounded number of attempts. Nothing was recorded under that key, so the repeat creates the order once a courier is free rather than replaying the refusal. A live store whose Plan Listo is not paid up is refused with 402 plan_required, and nothing is dispatched or debited. It is a different refusal from 402 insufficient_balance, which is about your balance: read error.code, never the status alone. error.message names the reason (no active plan, a charge that is pending, a plan that ended on a given date, a plan whose included deliveries for the cycle are all used, or a store that cannot request couriers right now), and it is not retryable. See Plan and billing. A dropoff beyond the store's 15 km service area is refused with 422 out_of_service_area, which is final. The courier network can also refuse the distance of a dropoff at the very edge of that circle, and the answer is the same 422, whether it refuses on the price or on the create itself. On the create it comes after the delivery was recorded, so the delivery stays, ended at no_courier with failure_reason: delivery_failed under your external_id, its fee debit returned in full and nothing else charged; replaying the request answers the same 422. Send that order to another address under a new external_id.\",\"url\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery\",\"mainEntityOfPage\":{\"@id\":\"https://docs.listo.delivery/api-reference/deliveries/create-a-delivery#webpage\"},\"dateModified\":\"2026-11-07T01:48:55.114Z\",\"publisher\":{\"@id\":\"https://docs.listo.delivery/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.listo.delivery#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Listo Partner API\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Listo Partner API","nav":{"global":null,"languages":[{"language":"en","default":true,"navbar":{"links":[{"href":"https://status.listo.delivery","label":"Status"}]},"groups":[{"group":"Get started","pages":[{"title":"Quickstart","href":"/en/quickstart"}]},{"group":"Guides","pages":[{"title":"Quote and create a delivery","href":"/en/guides/quote-and-create"},{"title":"Receive and verify webhooks","href":"/en/guides/webhooks"},{"title":"Debugging your integration","href":"/en/guides/debugging"},{"title":"Build with AI","href":"/en/guides/build-with-ai"},{"title":"Fund your balance and withdraw","href":"/en/guides/balance-and-withdrawals"},{"title":"Go live","href":"/en/guides/go-live"},{"title":"Connect a restaurant that already uses Listo","sidebarTitle":"Connect an existing account","href":"/en/guides/connect-existing-account"},{"title":"Plan and billing","href":"/en/guides/plan-and-billing"},{"title":"Connect Claude, Cursor or Codex","href":"/en/guides/connect-ai-client"}]},{"group":"Errors","expanded":false,"pages":[{"title":"Errors","href":"/en/errors/index"},{"title":"account_exists","href":"/en/errors/account_exists"},{"title":"address_not_found","href":"/en/errors/address_not_found"},{"title":"address_undeliverable","href":"/en/errors/address_undeliverable"},{"title":"cancel_failed","href":"/en/errors/cancel_failed"},{"title":"card_below_minimum","href":"/en/errors/card_below_minimum"},{"title":"card_not_ready","href":"/en/errors/card_not_ready"},{"title":"cash_over_cap","href":"/en/errors/cash_over_cap"},{"title":"charge_in_progress","href":"/en/errors/charge_in_progress"},{"title":"code_expired","href":"/en/errors/code_expired"},{"title":"console_access_required","href":"/en/errors/console_access_required"},{"title":"event_not_resendable","href":"/en/errors/event_not_resendable"},{"title":"idempotency_conflict","href":"/en/errors/idempotency_conflict"},{"title":"insufficient_balance","href":"/en/errors/insufficient_balance"},{"title":"internal_error","href":"/en/errors/internal_error"},{"title":"invalid_api_key","href":"/en/errors/invalid_api_key"},{"title":"no_couriers_available","href":"/en/errors/no_couriers_available"},{"title":"not_cancellable","href":"/en/errors/not_cancellable"},{"title":"not_editable","href":"/en/errors/not_editable"},{"title":"not_found","href":"/en/errors/not_found"},{"title":"not_reactivatable","href":"/en/errors/not_reactivatable"},{"title":"not_refundable","href":"/en/errors/not_refundable"},{"title":"not_reschedulable","href":"/en/errors/not_reschedulable"},{"title":"not_retriable","href":"/en/errors/not_retriable"},{"title":"order_too_large","href":"/en/errors/order_too_large"},{"title":"out_of_service_area","href":"/en/errors/out_of_service_area"},{"title":"payout_account_not_ready","href":"/en/errors/payout_account_not_ready"},{"title":"pickup_point_pending","href":"/en/errors/pickup_point_pending"},{"title":"plan_ended","href":"/en/errors/plan_ended"},{"title":"plan_required","href":"/en/errors/plan_required"},{"title":"provider_unavailable","href":"/en/errors/provider_unavailable"},{"title":"quote_consumed","href":"/en/errors/quote_consumed"},{"title":"quote_expired","href":"/en/errors/quote_expired"},{"title":"rate_limited","href":"/en/errors/rate_limited"},{"title":"read_only_key","href":"/en/errors/read_only_key"},{"title":"refund_amount_too_large","href":"/en/errors/refund_amount_too_large"},{"title":"resend_limit_reached","href":"/en/errors/resend_limit_reached"},{"title":"same_day_below_minimum","href":"/en/errors/same_day_below_minimum"},{"title":"same_day_cap_exceeded","href":"/en/errors/same_day_cap_exceeded"},{"title":"test_webhook_limit_reached","href":"/en/errors/test_webhook_limit_reached"},{"title":"unsupported_country","href":"/en/errors/unsupported_country"},{"title":"validation_failed","href":"/en/errors/validation_failed"},{"title":"webhook_endpoint_not_configured","href":"/en/errors/webhook_endpoint_not_configured"}]},{"group":"Reference","pages":[{"group":"Balance","pages":[{"title":"Read your prepaid balance","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/balance","href":"/api-reference/balance/read-your-prepaid-balance"},{"title":"Read a store's balance","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/stores/{store_id}/balance","href":"/api-reference/balance/read-a-stores-balance"}]},{"group":"Account connection","pages":[{"title":"Ask a restaurant to connect its account","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/connect_requests","href":"/api-reference/account-connection/ask-a-restaurant-to-connect-its-account"},{"title":"Exchange an approval for the store","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/connections","href":"/api-reference/account-connection/exchange-an-approval-for-the-store"}]},{"group":"Deliveries","pages":[{"title":"List deliveries","description":"Your deliveries, newest first, keyset-paginated the same way stores are.\nFilter by `status`, `payment_status`, `store_id`, `external_id`,\n`created_after` and `created_before`.\n\nThis is also the recovery path after a webhook outage. Re-read the\ndeliveries whose state you may have missed; the object you get back is\nthe same one the webhook would have carried, and it is where things\nstand now. You can send one event again with\n`POST /v1/events/{event_id}/resend`, but a resent event carries the\nsnapshot from the moment it happened, so a day-old status change\ndescribes a state the order has since left. Use a resend to debug a\nhandler, and this list to catch up.\n","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/deliveries","href":"/api-reference/deliveries/list-deliveries"},{"title":"Create a delivery","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/deliveries","href":"/api-reference/deliveries/create-a-delivery"},{"title":"Cancel a delivery","description":"Cancels a delivery. Valid from every status except `delivered`,\n`canceled`, `returned` and `returning`.\n\n`returning` is refused for the same substance as the three terminal\nones even though it is not terminal: the drop-off has already failed,\nthe courier is already carrying the order back, and both trips are\nalready charged. There is nothing left to call off, so it answers\n`409 not_cancellable`.\n\nThe courier is called off BEFORE anything changes on our side, so a\ncancellation that fails leaves the delivery live and untouched rather\nthan telling you it stopped while a courier is still driving. That case\nanswers `409 cancel_failed` with `retryable: true`.\n\nCancelling an already-cancelled delivery is not an error: it answers\n`200` with the current object.\n\nCancellation charges depend on how far the trip had progressed and are\nposted to your balance as a separate ledger entry, never folded into the\ndelivery's fee.\n","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/deliveries/{delivery_id}/cancellations","href":"/api-reference/deliveries/cancel-a-delivery"},{"title":"Refund a delivery","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/deliveries/{delivery_id}/refunds","href":"/api-reference/deliveries/refund-a-delivery"},{"title":"Retry a failed dispatch","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/deliveries/{delivery_id}/retries","href":"/api-reference/deliveries/retry-a-failed-dispatch"},{"title":"Fetch a delivery","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/deliveries/{id}","href":"/api-reference/deliveries/fetch-a-delivery"},{"title":"Update a delivery","deprecated":null,"version":null,"openapi":"/openapi/en.yaml patch /v1/deliveries/{id}","href":"/api-reference/deliveries/update-a-delivery"}]},{"group":"Events","pages":[{"title":"List your webhook events","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/events","href":"/api-reference/events/list-your-webhook-events"},{"title":"Send a webhook event again","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/events/{event_id}/resend","href":"/api-reference/events/send-a-webhook-event-again"},{"title":"Retrieve a webhook event","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/events/{id}","href":"/api-reference/events/retrieve-a-webhook-event"},{"title":"Send a test webhook","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/test_webhooks","href":"/api-reference/events/send-a-test-webhook"}]},{"group":"Ledger","pages":[{"title":"List your ledger entries","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/ledger","href":"/api-reference/ledger/list-your-ledger-entries"},{"title":"List a store's ledger entries","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/stores/{store_id}/ledger","href":"/api-reference/ledger/list-a-stores-ledger-entries"}]},{"group":"Authentication","pages":[{"title":"Check that your API key works","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/ping","href":"/api-reference/authentication/check-that-your-api-key-works"}]},{"group":"Quotes","pages":[{"title":"Price a delivery","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/quotes","href":"/api-reference/quotes/price-a-delivery"}]},{"group":"Request logs","pages":[{"title":"List your request log","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/request_logs","href":"/api-reference/request-logs/list-your-request-log"},{"title":"Retrieve a request log entry","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/request_logs/{id}","href":"/api-reference/request-logs/retrieve-a-request-log-entry"}]},{"group":"Stores","pages":[{"title":"List your stores","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/stores","href":"/api-reference/stores/list-your-stores"},{"title":"Create a store","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/stores","href":"/api-reference/stores/create-a-store"},{"title":"Fetch a store","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/stores/{id}","href":"/api-reference/stores/fetch-a-store"},{"title":"Update a store","deprecated":null,"version":null,"openapi":"/openapi/en.yaml patch /v1/stores/{id}","href":"/api-reference/stores/update-a-store"},{"title":"Invite a store owner","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/stores/{store_id}/owner","href":"/api-reference/stores/invite-a-store-owner"},{"title":"Reactivate a store's plan","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/stores/{store_id}/plan","href":"/api-reference/stores/reactivate-a-stores-plan"},{"title":"Cancel a store's plan","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/stores/{store_id}/plan_cancellation","href":"/api-reference/stores/cancel-a-stores-plan"},{"title":"Undo a store's plan cancellation","deprecated":null,"version":null,"openapi":"/openapi/en.yaml delete /v1/stores/{store_id}/plan_cancellation","href":"/api-reference/stores/undo-a-stores-plan-cancellation"}]},{"group":"Card onboarding","pages":[{"title":"Start card onboarding for a store","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/stores/{store_id}/stripe_onboarding","href":"/api-reference/card-onboarding/start-card-onboarding-for-a-store"},{"title":"Read a store's card readiness","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/stores/{store_id}/stripe_status","href":"/api-reference/card-onboarding/read-a-stores-card-readiness"},{"title":"Start card onboarding for your company","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/stripe_onboarding","href":"/api-reference/card-onboarding/start-card-onboarding-for-your-company"},{"title":"Read your company's card readiness","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/stripe_status","href":"/api-reference/card-onboarding/read-your-companys-card-readiness"}]},{"group":"Webhook deliveries","pages":[{"title":"List webhook delivery attempts","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/webhook_deliveries","href":"/api-reference/webhook-deliveries/list-webhook-delivery-attempts"}]},{"group":"Withdrawals","pages":[{"title":"List your withdrawals","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/withdrawals","href":"/api-reference/withdrawals/list-your-withdrawals"},{"title":"Request a withdrawal","deprecated":null,"version":null,"openapi":"/openapi/en.yaml post /v1/withdrawals","href":"/api-reference/withdrawals/request-a-withdrawal"},{"title":"Fetch a withdrawal","deprecated":null,"version":null,"openapi":"/openapi/en.yaml get /v1/withdrawals/{id}","href":"/api-reference/withdrawals/fetch-a-withdrawal"}]},{"group":"Webhooks","pages":[{"title":"A delivery reached a new status","version":null,"deprecated":null,"openapi":"/openapi/en.yaml webhook delivery.status_changed","href":"/api-reference/webhooks/a-delivery-reached-a-new-status"},{"title":"A delivery changed without changing status","version":null,"deprecated":null,"openapi":"/openapi/en.yaml webhook delivery.updated","href":"/api-reference/webhooks/a-delivery-changed-without-changing-status"},{"title":"The courier moved","version":null,"deprecated":null,"openapi":"/openapi/en.yaml webhook delivery.courier_location","href":"/api-reference/webhooks/the-courier-moved"},{"title":"A withdrawal was settled","version":null,"deprecated":null,"openapi":"/openapi/en.yaml webhook withdrawal.updated","href":"/api-reference/webhooks/a-withdrawal-was-settled"},{"title":"A store changed","version":null,"deprecated":null,"openapi":"/openapi/en.yaml webhook store.updated","href":"/api-reference/webhooks/a-store-changed"},{"title":"A restaurant connected its existing account to you","version":null,"deprecated":null,"openapi":"/openapi/en.yaml webhook store.connected","href":"/api-reference/webhooks/a-restaurant-connected-its-existing-account-to-you"}]}]},{"group":"Concepts","pages":[{"title":"The money model","href":"/en/concepts/money"},{"title":"Where a delivery came from","href":"/en/concepts/delivery-origin"},{"title":"Test mode","href":"/en/concepts/test-mode"},{"title":"Signing","href":"/en/concepts/signing"},{"title":"Cash at the door","href":"/en/concepts/cash-at-the-door"}]},{"group":"Changelog","pages":[{"title":"Changelog","href":"/en/changelog"}]},{"group":"Legal","pages":[{"title":"Terms for Partners","url":"https://listo.delivery/terminos-partners","href":"/en/terms-for-partners"}]}]},{"language":"es","navbar":{"links":[{"href":"https://status.listo.delivery","label":"Estado del servicio"}]},"pages":[{"title":"es","href":"/es/api-reference/deliveries/create-a-delivery"}]}]},"basePath":"","title":"Create a delivery - Listo Partner API","buildId":"dpl_8LJbrAiZvr1K98WQNZXCyY5AvT34","deploymentId":"dpl_8LJbrAiZvr1K98WQNZXCyY5AvT34","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-i2a3QZ8E.js","utils-ChLwp8m0.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-DRktVTDc.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-CA2pEs4T.js","snippetPresets-ClQ6XrKD.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-Ct_Lv36y.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-Cn2PYw9e.js","shallow-rkyymjXQ.js","getApiPlaygroundDisplay-BB9vkXtE.js","CodeSnippet-Bm5RU7m1.js","CodeSnippets-BgU9Deuz.js","Page-BUDYNlhG.js","getPastedJson-BD-6NNum.js","icon-8Z6tud2e.js","MethodPill-Tuzr4g3V.js","DuplicateIcon-0ceWFYhN.js","createIcon-FBXow_ST.js","DynamicMarkdown-CD8713Yy.js","SectionHeading-7zgzQbH-.js","axios-vUo_bRtp.js","Markdown-D7Ofw1oz.js","clsx-eKJ4rcii.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-CwjSCXIK.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-9EK5IAXV.js"]}