> ## Documentation Index
> Fetch the complete documentation index at: https://docs.listo.delivery/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent 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 <API 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.

# Where a delivery came from

> Why deliveries you did not create appear at your stores, and how to tell them apart.

Not every delivery at your stores was created by you. A store whose owner can
sign in to the merchant console can dispatch an order from there by hand, and a
store that takes orders from its customers on WhatsApp has the delivery for each
one created when it accepts the order. Either delivery belongs to your account
like any other: it is in your list and it fires your webhooks.

`origin` is the field that says which of the three happened.

```json theme={null}
{
  "id": "dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe",
  "external_id": null,
  "origin": "console"
}
```

| `origin` | Who created the delivery |
| - | - |
| `api` | You, through `POST /v1/deliveries` |
| `console` | The store itself, from the merchant console |
| `whatsapp` | A customer who ordered from the store in a WhatsApp chat, once the store accepted the order |

Those three values are the whole set. Every delivery carries one, it is always a
string and never `null`, and it never changes: a delivery is created once, by
one of the three, so the answer is fixed the moment the delivery exists. A
`no_courier` delivery you retry keeps it, along with its id and your
`external_id`.

## A console or WhatsApp order is not a bug in your integration

A console order reaches you with `origin: "console"` and `external_id: null`,
and a WhatsApp order with `origin: "whatsapp"` and `external_id: null`. Neither
will match anything in your database, because you never put it there. That is
what these orders look like, not a sign that something went wrong.

Every platform where a person and a program create the same kind of record works
this way. A charge somebody enters by hand in a dashboard still reaches the
webhooks of the system integrated with it, for the same reason: the record is
real, the account is yours, and being told about only half of what happened on
it would leave your books short.

So render them or ignore them, whichever suits what you are building. What you
should not do is treat one as a failure: no alert, no dead letter queue, and no
error status back to a signed event you understood perfectly well.

One habit to break first. Do not tell your orders from the others by looking for
a missing `external_id`. It happens to line up, because the API requires one on
every create and neither the console nor a WhatsApp order has a field for it, but
it is a guess assembled from another field, and `origin` is the field that
answers the question.

## Whose money a console or WhatsApp order spends

A store can dispatch from the console only when it owns its money, so a console
order always spends that store's own balance and never yours. Your share of the
fee is debited there when the delivery is created and cash the courier collects
is credited there when it arrives, exactly as they are for an order you created
through the API at the same store.

A WhatsApp order is billed by the same rule as every other delivery at its store,
so at a store that owns its money it spends that store's balance and not yours,
with the same debit when the delivery is created and the same credit for cash
when it arrives.

Where a store's money lives and who may sign in are one setting, `access_tier`,
and [the money model](https://docs.listo.delivery/en/concepts/money.md) has the table. Only a `full`
store can produce a console order: at `api_only` nobody can sign in at all, and
at `wallet` the owner reaches the balance pages rather than dispatch. A WhatsApp
order has the same limit one step earlier: a store takes orders on WhatsApp only
after its owner connects a number in the console's assistant settings, and only
a `full` store can open them. So a delivery created while a store is at either
of those tiers has `origin` `"api"`. A store's tier can change and a delivery
keeps the `origin` it was created with, so read `origin` off the delivery and
never infer it from the store's current tier.

Accepting does not need a person in the console. A store can have its WhatsApp
orders accepted automatically, so a `whatsapp` delivery can appear while nobody
is signed in.

That is worth knowing in both directions. If no store of yours is on `full`
today, you will never see a console or WhatsApp order, and handling for them
costs nothing to leave in against the day one of them is.

## Asking for only your own orders

`GET /v1/deliveries?origin=api` lists only the deliveries you created,
`origin=console` lists only those the stores dispatched themselves, and
`origin=whatsapp` lists only those their customers ordered on WhatsApp. It
combines with the other filters, so "mine, at this store, still on the road" is
one request rather than a walk through every page discarding rows as they arrive.
[`GET /v1/deliveries`](https://docs.listo.delivery/api-reference/deliveries/list-deliveries) has the full
set of filters.

The webhook stream has no equivalent. Every delivery at your stores fires
events, by design, so a subscription cannot be narrowed to one origin and the
filtering happens in your handler instead. Read `origin` off `data` and answer
`2xx` either way: the event was delivered correctly, and anything else earns you
six retries of an order you had already decided to ignore.

## Where `origin` appears

* On the delivery object wherever it is returned: the create, a read, the list,
  a cancellation and a retry.
* On `delivery.status_changed` and `delivery.updated`, whose `data` is that same
  whole object.
* Not on `delivery.courier_location`. Its body is the slim position object and
  carries no delivery fields beyond the ids, so read the delivery back or use
  what you already stored.
* Not on a quote. A quote is a price rather than an order, so it has nothing to
  have come from yet.

How to be told about any of this without polling is
[webhooks](https://docs.listo.delivery/en/guides/webhooks.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.