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

# Read your prepaid balance

> Your prepaid balance for the world the key acts in.

`balance_cents` is everything in the account. `available_cents` is what
you can withdraw today, which is lower whenever cash collected at
customers' doors has not reached us yet. Spending is not gated on
`available_cents`; withdrawing is.

`in_review_cents` is the part of `balance_cents` that is cash from an
order we are still reviewing. It is always present, `0` when nothing is
under review, and it is already left out of `available_cents`. It can
not be advanced, and a delivery cannot be paid from it, so a create can
answer `402 insufficient_balance` while `balance_cents` alone looks like
enough: `balance_cents` minus `in_review_cents` is what deliveries can
draw on. It moves when we have an answer, not on a date.

### The same-day offer

`same_day` describes what a `same_day` withdrawal
(`POST /v1/withdrawals`) would cost you right now and how large a one
you could ask for, so you can render the choice instead of discovering
it from a refusal. Every field is always present.

`ceiling_cents` is the largest `amount_cents` a same-day request would
be accepted for at this moment. **It is not another name for
`available_cents`**: the ceiling is already net of `fee_cents` and
already clamped to what is left of the day, and it can reach cash still
on hold that `available_cents` excludes by definition. On one account it
is the larger of the two and on another the smaller, so neither is
derivable from the other.

`reach_cents` is everything the request could draw on before the fee,
and `advanceable_cents` is the part of that which is still-held cash.
`cap_cents`, `cap_used_cents` and `cap_remaining_cents` are the daily
maximum per account and today's position against it, and
`minimum_cents` is the floor per request.

`window_open` says whether staff are sending right now. `due_at` is the
deadline a request made at the moment you read this WOULD carry, and
`next_window_opens_at` is the next time sending resumes; the deadline
your request actually carries is the `due_at` on the withdrawal object
it returns. Requests are accepted at any hour either way.

`offerable` is false when no same-day request would be accepted at all,
and `unavailable_reason` says which of the two states you are in:
`cap_reached` (today's maximum is spent, and it returns tomorrow
morning) or `below_minimum` (the reach is too small to clear the floor
plus the fee). It is null whenever `offerable` is true.




## OpenAPI

````yaml /openapi/en.yaml get /v1/balance
openapi: 3.1.0
info:
  title: Listo Delivery API
  version: 1.34.0
  description: |
    Quote a delivery, dispatch it, and follow it to the door.

    Every request authenticates with a bearer API key. The key itself decides
    the world it acts in: a test key dispatches simulated couriers and moves no
    money, a live key dispatches real ones. A key can also be read-only: it
    may call GET and HEAD and nothing else, and any other call answers
    `403 read_only_key`. Money is always in Mexican pesos and
    always in integer cents, and every timestamp is ISO-8601 in UTC.

    Ids are opaque prefixed strings (`st_` a store, `qt_` a quote, `dlv_` a
    delivery, `wd_` a withdrawal, `evt_` an event, `le_` a ledger entry,
    `whd_` a webhook delivery attempt, `req_` a logged request). Treat
    them as strings of unbounded length and never parse them.

    Changes to v1 are additive: new fields and new enum values can appear, so
    ignore what you do not recognise rather than failing on it.
servers:
  - url: https://api.listo.delivery
security: []
tags:
  - name: Authentication
    description: Checking that a key works before you build against it.
  - name: Stores
    description: The pickup locations you dispatch from.
  - name: Account connection
    description: Connecting a restaurant's existing Listo account to you.
  - name: Quotes
    description: What a delivery costs, priced and held.
  - name: Deliveries
    description: Dispatching an order and following it to the door.
  - name: Balance
    description: The prepaid balance your deliveries draw on.
  - name: Withdrawals
    description: Moving money out of that balance to your bank.
  - name: Card onboarding
    description: Connecting the account that takes card payments.
  - name: Events
    description: >-
      The events Listo sent you, resending one, and sending a test event to your
      endpoint.
  - name: Webhook deliveries
    description: >-
      Every attempt Listo made to send an event to your endpoint, and what came
      back.
  - name: Request logs
    description: >-
      Every call your keys made to this API, with what you sent and what we
      answered.
paths:
  /v1/balance:
    get:
      tags:
        - Balance
      summary: Read your prepaid balance
      description: |
        Your prepaid balance for the world the key acts in.

        `balance_cents` is everything in the account. `available_cents` is what
        you can withdraw today, which is lower whenever cash collected at
        customers' doors has not reached us yet. Spending is not gated on
        `available_cents`; withdrawing is.

        `in_review_cents` is the part of `balance_cents` that is cash from an
        order we are still reviewing. It is always present, `0` when nothing is
        under review, and it is already left out of `available_cents`. It can
        not be advanced, and a delivery cannot be paid from it, so a create can
        answer `402 insufficient_balance` while `balance_cents` alone looks like
        enough: `balance_cents` minus `in_review_cents` is what deliveries can
        draw on. It moves when we have an answer, not on a date.

        ### The same-day offer

        `same_day` describes what a `same_day` withdrawal
        (`POST /v1/withdrawals`) would cost you right now and how large a one
        you could ask for, so you can render the choice instead of discovering
        it from a refusal. Every field is always present.

        `ceiling_cents` is the largest `amount_cents` a same-day request would
        be accepted for at this moment. **It is not another name for
        `available_cents`**: the ceiling is already net of `fee_cents` and
        already clamped to what is left of the day, and it can reach cash still
        on hold that `available_cents` excludes by definition. On one account it
        is the larger of the two and on another the smaller, so neither is
        derivable from the other.

        `reach_cents` is everything the request could draw on before the fee,
        and `advanceable_cents` is the part of that which is still-held cash.
        `cap_cents`, `cap_used_cents` and `cap_remaining_cents` are the daily
        maximum per account and today's position against it, and
        `minimum_cents` is the floor per request.

        `window_open` says whether staff are sending right now. `due_at` is the
        deadline a request made at the moment you read this WOULD carry, and
        `next_window_opens_at` is the next time sending resumes; the deadline
        your request actually carries is the `due_at` on the withdrawal object
        it returns. Requests are accepted at any hour either way.

        `offerable` is false when no same-day request would be accepted at all,
        and `unavailable_reason` says which of the two states you are in:
        `cap_reached` (today's maximum is spent, and it returns tomorrow
        morning) or `below_minimum` (the reach is too small to clear the floor
        plus the fee). It is null whenever `offerable` is true.
      operationId: getBalance
      responses:
        '200':
          description: The request succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Balance'
              example:
                balance_cents: 400000
                available_cents: 268400
                currency: MXN
                same_day:
                  fee_cents: 3000
                  minimum_cents: 10000
                  ceiling_cents: 397000
                  reach_cents: 400000
                  advanceable_cents: 131600
                  cap_cents: 500000
                  cap_used_cents: 0
                  cap_remaining_cents: 500000
                  window_open: true
                  due_at: '2026-07-30T21:30:00Z'
                  next_window_opens_at: '2026-07-31T14:00:00Z'
                  offerable: true
                  unavailable_reason: null
                in_review_cents: 0
        '401':
          description: >-
            The API key is missing, revoked, or not valid for this mode
            (`invalid_api_key`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              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
      security:
        - BearerAuth: []
components:
  schemas:
    Balance:
      type: object
      description: Your prepaid balance for the world the key acts in.
      properties:
        balance_cents:
          type: integer
          description: Everything in the account, in centavos.
        available_cents:
          type: integer
          description: What you can withdraw today. Spending is not gated on it.
        currency:
          type: string
          enum:
            - MXN
        same_day:
          $ref: '#/components/schemas/SameDayTerms'
        in_review_cents:
          type: integer
          description: |
            The part of `balance_cents` that is cash from an order we are still
            reviewing. `0` when nothing is under review. Already left out of
            `available_cents`, and a delivery cannot be paid from it.
      required:
        - balance_cents
        - available_cents
        - in_review_cents
        - currency
        - same_day
    ErrorResponse:
      type: object
      description: The body of every 4xx and 5xx answer on this API.
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error
    SameDayTerms:
      type: object
      description: |
        What a `same_day` withdrawal would cost you and how large a one you
        could ask for, right now. Every field is always present.
      properties:
        fee_cents:
          type: integer
          description: >-
            What a same-day withdrawal costs, IVA included, charged on top of
            the amount.
        minimum_cents:
          type: integer
          description: The smallest `amount_cents` a same-day request is accepted for.
        ceiling_cents:
          type: integer
          description: |
            The largest `amount_cents` a same-day request would be accepted for
            at this moment. Not another name for `available_cents`.
        reach_cents:
          type: integer
          description: Everything a request could draw on before the fee.
        advanceable_cents:
          type: integer
          description: The part of `reach_cents` that is still-held cash.
        cap_cents:
          type: integer
          description: The daily maximum per account.
        cap_used_cents:
          type: integer
          description: How much of the daily maximum is already used today.
        cap_remaining_cents:
          type: integer
          description: What is left of the daily maximum today.
        window_open:
          type: boolean
          description: Whether staff are sending transfers right now.
        due_at:
          type: string
          format: date-time
          description: The deadline a request made at the moment you read this would carry.
        next_window_opens_at:
          type: string
          format: date-time
          description: The next time sending resumes.
        offerable:
          type: boolean
          description: False when no same-day request would be accepted at all.
        unavailable_reason:
          type:
            - string
            - 'null'
          enum:
            - cap_reached
            - below_minimum
            - null
          description: |
            Why `offerable` is false: `cap_reached` (today's maximum is spent)
            or `below_minimum` (the reach is too small to clear the floor plus
            the fee). Null whenever `offerable` is true.
      required:
        - fee_cents
        - minimum_cents
        - ceiling_cents
        - reach_cents
        - advanceable_cents
        - cap_cents
        - cap_used_cents
        - cap_remaining_cents
        - window_open
        - due_at
        - next_window_opens_at
        - offerable
        - unavailable_reason
    Error:
      type: object
      description: |
        What every refusal and every failure looks like. `code` is the only
        field to branch on; `message` is for a human reading a log and may
        change wording or language without notice.
      properties:
        code:
          type: string
          description: |
            A stable, machine-readable name for what went wrong, such as
            `validation_failed`, `not_found` or `insufficient_balance`. Branch
            on this and never on `message`. The set grows additively: handle a
            code you do not know as the generic failure its status code says it
            is. Every code has a page under `doc_url`.
          example: validation_failed
        message:
          type: string
          description: |
            A sentence for the person debugging. Free to change wording or
            language, so never match on it.
          example: order_value_cents is required.
        param:
          type:
            - string
            - 'null'
          description: |
            The request field the refusal is about, when one applies, spelled
            as you sent it (`dropoff.street`, `manifest.items`). Null when the
            failure is not about one field.
          example: order_value_cents
        retryable:
          type: boolean
          description: |
            Whether sending the identical request again could succeed. `false`
            means it never will without a change on your side or in the world.
          example: false
        doc_url:
          type: string
          format: uri
          description: The documentation page for this `code`.
          example: https://docs.listo.delivery/en/errors/validation_failed
        request_id:
          type: string
          description: |
            Our id for this request, the same value as the `X-Request-Id`
            response header. Quote it to support, and filter
            `GET /v1/request_logs` by it.
          example: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      required:
        - code
        - message
        - param
        - retryable
        - doc_url
        - request_id
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        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.

````

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