Skip to main content
GET
Fetch a delivery

Authorizations

Authorization
string
header
required

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.

Path Parameters

id
string
required

Response

The request succeeded.

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.

id
string
required

The delivery id, dlv_ prefixed.

Example:

"dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe"

external_id
string | null
required

Your own order reference, echoed back exactly as you sent it.

Example:

"order-841"

origin
enum<string>
required

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.

Available options:
api,
console,
whatsapp
Example:

"api"

code
string
required

The human order code the courier and our support quote. Unique per store, not globally, so never address an order by it.

Example:

"LS-2048"

status
enum<string>
required

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.

Available options:
awaiting_payment,
awaiting_location,
scheduled,
dispatching,
courier_assigned,
picking_up,
en_route,
returning,
delivered,
returned,
canceled,
no_courier
store_id
string
required

The st_ id of the store the courier collects from.

test
boolean
required

Whether this order lives in the sandbox. Derived from the store, so there is no field to set: a test key can only reach test stores.

payment
object
required

How the customer pays, and where that got to.

order_value_cents
integer | null
required

The goods value you sent, untouched.

fee_cents
integer
required

The total delivery fee.

business_fee_cents
integer
required

The part of fee_cents charged to you.

customer_fee_cents
integer
required

The part of fee_cents charged to the customer. Always sums with business_fee_cents back to fee_cents.

tip_cents
integer
required

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.

currency
enum<string>
required
Available options:
MXN
quote_id
string | null
required

The qt_ quote this order bound, so you can tie the price you were quoted to the order that spent it. Null when the order was priced inline instead.

pickup
object
required

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.

dropoff
object
required

Where the courier delivers, echoed back as you sent it.

manifest
object | null
required

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.

vehicle
enum<string>
required

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.

Available options:
moto,
car
Example:

"moto"

courier
object | null
required

Null until a courier is assigned, and then FROZEN: a terminal delivery keeps reporting who carried it.

pickup_eta_at
string<date-time> | null
required

When the courier is expected at the store. Absolute, so you compute your own countdown; a countdown computed here is stale before you read it.

dropoff_eta_at
string<date-time> | null
required

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.

tracking_url
string<uri> | null
required

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.

Example:

"https://app.listo.delivery/t/abc123"

verification
object | null
required

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.

return_verification
object
required

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
object | null
required

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.

proofs
object
required

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.

cancellation
object | null
required

Who called the order off, and why. Null on an order that was not cancelled.

failure_reason
enum<string> | null
required

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.

Available options:
no_couriers_available,
address_unreachable,
customer_unavailable,
delivery_failed,
null
charges
object[]
required

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.

refunds
object[]
required

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.

refunded_cents
integer
required

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.

refundable_cents
integer
required

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.

support_reference
string | null
required

A short handle to quote when you escalate an order to us. Null until the order reaches dispatch.

Example:

"2080F"

pickup_ready_at
string<date-time> | null
required

The scheduled pickup time you asked for. Null on a dispatch-now order.

requested_at
string<date-time> | null
required
courier_assigned_at
string<date-time> | null
required

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.

picked_up_at
string<date-time> | null
required
delivered_at
string<date-time> | null
required
canceled_at
string<date-time> | null
required
returned_at
string<date-time> | null
required
created_at
string<date-time>
required
updated_at
string<date-time>
required