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

# Solicita un retiro

> Solicita una transferencia de `amount_cents` desde tu saldo a tu cuenta bancaria registrada. El dinero se reserva en el momento en que se acepta la solicitud, así que una segunda solicitud no puede gastarlo dos veces.

La solicitud la liquida una persona, normalmente dentro de un día hábil, y el resultado llega como un webhook `withdrawal.updated` y no en esta respuesta. Pedir más que `available_cents` es un `402`.

Necesitas una cuenta bancaria registrada antes de tu primer retiro, desde el [panel](https://partners.listo.delivery) y no desde esta API. Sin una, esta llamada responde `422 payout_account_not_ready` y no se reserva nada. El mismo rechazo aplica al día después de que CAMBIAS la cuenta: un destino recién cambiado no puede recibir dinero durante 24 horas, y el `message` indica el momento en que vuelve a poder hacerlo.

### Retiros el mismo día

`kind` es opcional y es `standard` si lo omites, que es el camino gratuito de arriba y no cambia. Envía `"kind": "same_day"` para que se envíe HOY:

- Cuesta una tarifa fija, con IVA incluido, que la respuesta reporta como `fee_cents`. La tarifa es un SEGUNDO cargo y nunca una deducción, así que tu banco recibe todo el `amount_cents` y tu saldo pierde los dos juntos.
- Puede usar efectivo que todavía está retenido, algo que el camino gratuito no puede, así que `available_cents` no es el límite aquí.
- Las solicitudes se aceptan a cualquier hora. Una hecha entre las 8 am y las 8 pm de la Ciudad de México se envía dentro de 3 horas; una hecha fuera de ese horario sale a la mañana siguiente. `due_at` en la respuesta es la fecha límite que tu solicitud lleva realmente, fijada en el momento en que la hiciste.
- Hay un mínimo por solicitud y un máximo diario por cuenta.

Tres rechazos son específicos de este camino y cada uno nombra el límite en `message`: `402 insufficient_balance` cuando el monto más la tarifa es más de lo que se puede retirar hoy, `422 same_day_cap_exceeded` cuando el máximo del día no tiene lugar para él, y `422 same_day_below_minimum` cuando el monto está por debajo del mínimo. Un `kind` que no es ninguna de las dos palabras es `422 validation_failed` con `param: "kind"`, nunca un retorno silencioso al camino gratuito.




## OpenAPI

````yaml /openapi/es.yaml post /v1/withdrawals
openapi: 3.1.0
info:
  title: Listo Delivery API
  version: 1.34.0
  description: >
    Cotiza un envío, despáchalo y sigue el recorrido hasta la puerta.


    Cada solicitud se autentica con una llave de API bearer. La llave misma
    decide el modo en el que actúa: una llave de prueba despacha repartidores
    simulados y no mueve dinero, y una llave real despacha repartidores reales.
    Una llave también puede ser de solo lectura: solo puede llamar a GET y HEAD,
    y cualquier otra llamada responde `403 read_only_key`. El dinero siempre va
    en pesos mexicanos y siempre en centavos enteros, y cada marca de tiempo es
    ISO-8601 en UTC.


    Los ids son cadenas opacas con prefijo (`st_` una tienda, `qt_` una
    cotización, `dlv_` un

    envío, `wd_` un retiro, `evt_` un evento, `le_` un movimiento del historial,

    `whd_` un intento de entrega de webhook, `req_` una llamada registrada).
    Trátalos

    como cadenas de longitud ilimitada y nunca los analices.


    Los cambios a v1 son aditivos: pueden aparecer campos nuevos y valores
    nuevos de enumeración, así que ignora lo que no reconozcas en lugar de
    fallar.
servers:
  - url: https://api.listo.delivery
security: []
tags:
  - name: Authentication
    description: Verifica que una llave funciona antes de empezar a construir con ella.
    x-group: Autenticación
  - name: Stores
    description: Las ubicaciones de recolección desde las que despachas.
    x-group: Tiendas
  - name: Account connection
    description: Conectar la cuenta de Listo que ya tiene un restaurante contigo.
    x-group: Conexión de cuenta
  - name: Quotes
    description: Lo que cuesta un envío, cotizado y retenido.
    x-group: Cotizaciones
  - name: Deliveries
    description: Despachar un pedido y seguirlo hasta la puerta.
    x-group: Envíos
  - name: Balance
    description: El saldo prepagado del que salen tus envíos.
    x-group: Saldo
  - name: Withdrawals
    description: Sacar dinero de ese saldo hacia tu banco.
    x-group: Retiros
  - name: Card onboarding
    description: Conectar la cuenta que recibe los pagos con tarjeta.
    x-group: Alta de cobros con tarjeta
  - name: Events
    description: >-
      Los eventos que Listo te envió, al reenviar uno y al enviar un evento de
      prueba a tu endpoint.
    x-group: Eventos
  - name: Webhook deliveries
    description: >-
      Cada intento que hizo Listo de enviar un evento a tu endpoint, y lo que
      respondió tu endpoint.
    x-group: Entregas de webhook
  - name: Request logs
    description: >-
      Cada llamada que hicieron tus llaves a esta API, con lo que enviaste y con
      lo que respondimos.
    x-group: Registro de solicitudes
  - name: Ledger
    x-group: Historial
  - name: Webhooks
    x-group: Webhooks
paths:
  /v1/withdrawals:
    post:
      tags:
        - Withdrawals
      summary: Solicita un retiro
      description: >
        Solicita una transferencia de `amount_cents` desde tu saldo a tu cuenta
        bancaria registrada. El dinero se reserva en el momento en que se acepta
        la solicitud, así que una segunda solicitud no puede gastarlo dos veces.


        La solicitud la liquida una persona, normalmente dentro de un día hábil,
        y el resultado llega como un webhook `withdrawal.updated` y no en esta
        respuesta. Pedir más que `available_cents` es un `402`.


        Necesitas una cuenta bancaria registrada antes de tu primer retiro,
        desde el [panel](https://partners.listo.delivery) y no desde esta API.
        Sin una, esta llamada responde `422 payout_account_not_ready` y no se
        reserva nada. El mismo rechazo aplica al día después de que CAMBIAS la
        cuenta: un destino recién cambiado no puede recibir dinero durante 24
        horas, y el `message` indica el momento en que vuelve a poder hacerlo.


        ### Retiros el mismo día


        `kind` es opcional y es `standard` si lo omites, que es el camino
        gratuito de arriba y no cambia. Envía `"kind": "same_day"` para que se
        envíe HOY:


        - Cuesta una tarifa fija, con IVA incluido, que la respuesta reporta
        como `fee_cents`. La tarifa es un SEGUNDO cargo y nunca una deducción,
        así que tu banco recibe todo el `amount_cents` y tu saldo pierde los dos
        juntos.

        - Puede usar efectivo que todavía está retenido, algo que el camino
        gratuito no puede, así que `available_cents` no es el límite aquí.

        - Las solicitudes se aceptan a cualquier hora. Una hecha entre las 8 am
        y las 8 pm de la Ciudad de México se envía dentro de 3 horas; una hecha
        fuera de ese horario sale a la mañana siguiente. `due_at` en la
        respuesta es la fecha límite que tu solicitud lleva realmente, fijada en
        el momento en que la hiciste.

        - Hay un mínimo por solicitud y un máximo diario por cuenta.


        Tres rechazos son específicos de este camino y cada uno nombra el límite
        en `message`: `402 insufficient_balance` cuando el monto más la tarifa
        es más de lo que se puede retirar hoy, `422 same_day_cap_exceeded`
        cuando el máximo del día no tiene lugar para él, y `422
        same_day_below_minimum` cuando el monto está por debajo del mínimo. Un
        `kind` que no es ninguna de las dos palabras es `422 validation_failed`
        con `param: "kind"`, nunca un retorno silencioso al camino gratuito.
      operationId: createWithdrawal
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWithdrawalRequest'
            example:
              amount_cents: 250000
              kind: standard
        required: true
      responses:
        '201':
          description: Creado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Withdrawal'
              example:
                id: wd_2xN6bK9mR4tQ7vC1sZ8pLwYe
                amount_cents: 250000
                currency: MXN
                status: pending
                requested_at: '2026-07-30T18:30:00Z'
                resolved_at: null
                test: false
                clave_de_rastreo: null
                fee_cents: 0
                kind: standard
                due_at: null
        '401':
          description: >-
            La llave de API no existe, fue revocada o no es válida para este
            modo (`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
        '402':
          description: El saldo prepagado no cubre esta solicitud (`insufficient_balance`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                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
        '403':
          description: >-
            La llave es válida y es de solo lectura (`read_only_key`), así que
            puede leer pero no crear, cambiar ni cancelar nada. Usa una llave
            emitida sin la opción de solo lectura.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                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
        '422':
          description: >-
            La solicitud se entendió y se rechazó. `error.code` dice por qué y
            `error.param` nombra el campo con el problema cuando aplica.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: validation_failed
                  message: Amount cents must be an integer
                  param: amount_cents
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/validation_failed
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      security:
        - BearerAuth: []
components:
  schemas:
    CreateWithdrawalRequest:
      type: object
      description: Cuánto mover de tu saldo.
      properties:
        amount_cents:
          type: integer
          minimum: 1
          description: Lo que recibe tu banco, en centavos.
        kind:
          type:
            - string
            - 'null'
          enum:
            - standard
            - same_day
            - null
          description: Por omisión es `standard`.
      required:
        - amount_cents
    Withdrawal:
      type: object
      description: Una solicitud para mover dinero de tu saldo a tu cuenta bancaria.
      properties:
        id:
          type: string
          description: El id del retiro, con prefijo `wd_`.
          example: wd_2xN6bK9mR4tQ7vC1sZ8pLwYe
        amount_cents:
          type: integer
          description: >
            Lo que recibe tu banco. En un retiro `same_day` la tarifa es un
            SEGUNDO cargo en lugar de una deducción, así que esta cifra es lo
            que llega en cualquiera de los dos casos, y `fee_cents` es lo que tu
            saldo pierde encima de ella.
        fee_cents:
          type: integer
          description: >
            Cuánto costó el retiro, con IVA incluido. Siempre `0` en un retiro
            `standard`. Siempre está presente.
        currency:
          type: string
          enum:
            - MXN
        kind:
          type: string
          enum:
            - standard
            - same_day
          description: >
            `standard` es la vía gratuita: solo tu saldo liberado, enviado en 1
            a 3 días hábiles. `same_day` cuesta `fee_cents`, puede alcanzar
            efectivo que todavía está retenido, y se envía el mismo día dentro
            de los horarios indicados en `POST /v1/withdrawals`.
        status:
          type: string
          enum:
            - pending
            - paid
            - failed
          description: >
            `pending` hasta que una persona lo liquide. El paso a `paid` o
            `failed` es lo que anuncia `withdrawal.updated`.
        requested_at:
          type:
            - string
            - 'null'
          format: date-time
        due_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >
            Cuándo se espera que se haya enviado un retiro `same_day`, sellado
            cuando lo solicitaste y nunca recalculado después. Es null en cada
            retiro `standard`: la vía gratuita promete un rango de días hábiles
            en lugar de un instante.
        resolved_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Null mientras siga pendiente.
        clave_de_rastreo:
          type:
            - string
            - 'null'
          nullable: true
          description: >
            La clave de rastreo SPEI de la transferencia: el identificador que
            tu propio banco

            usa para localizar el depósito. Es con lo que concilias un pago

            contra un estado de cuenta bancario, y es la misma clave que ves en
            el

            panel de este retiro.


            Siempre está presente, y es null hasta que se envía la
            transferencia. Se queda en null

            para siempre en un retiro `failed`, porque nunca hubo transferencia.
          example: MBAN01002608120001234567
        test:
          type: boolean
      required:
        - id
        - amount_cents
        - fee_cents
        - currency
        - kind
        - status
        - requested_at
        - due_at
        - resolved_at
        - clave_de_rastreo
        - test
    ErrorResponse:
      type: object
      description: El cuerpo de toda respuesta 4xx y 5xx de esta API.
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error
    Error:
      type: object
      description: >
        Cómo se ve todo rechazo y toda falla. `code` es el único

        campo en el que decidir; `message` es para una persona que lee un
        registro y puede

        cambiar de redacción o de idioma sin aviso.
      properties:
        code:
          type: string
          description: >
            Un nombre estable y legible por máquina de lo que salió mal, como

            `validation_failed`, `not_found` o `insufficient_balance`. Decide

            con base en esto y nunca en `message`. La lista crece sin romper
            nada: trata

            un código que no conozcas como el fallo genérico que indica su
            código de estado.

            Cada código tiene una página en `doc_url`.
          example: validation_failed
        message:
          type: string
          description: |
            Una oración para quien depura. Puede cambiar de redacción o de
            idioma en cualquier momento, así que nunca la uses para comparar.
          example: order_value_cents is required.
        param:
          type:
            - string
            - 'null'
          description: >
            El campo de la solicitud al que se refiere el rechazo, cuando
            aplica, escrito

            como lo enviaste (`dropoff.street`, `manifest.items`). Null cuando
            la

            falla no se refiere a un solo campo.
          example: order_value_cents
        retryable:
          type: boolean
          description: >
            Si volver a enviar la misma solicitud podría tener éxito. `false`

            significa que nunca lo tendrá sin un cambio de tu lado o en el
            mundo.
          example: false
        doc_url:
          type: string
          format: uri
          description: La página de documentación de este `code`.
          example: https://docs.listo.delivery/en/errors/validation_failed
        request_id:
          type: string
          description: >
            Nuestro id de esta solicitud, el mismo valor que el encabezado de
            respuesta

            `X-Request-Id`. Cítalo a soporte, y filtra

            `GET /v1/request_logs` por él.
          example: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      required:
        - code
        - message
        - param
        - retryable
        - doc_url
        - request_id
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        Tu llave de API, enviada como `Authorization: Bearer <key>`. Las llaves
        reales y de prueba son distintas; la llave que envías decide en qué modo
        actúa la llamada. Para probar llamadas, usa una llave de prueba (empieza
        con `lst_test_`): no se envía ningún repartidor real y no se mueve
        dinero real, pero los webhooks se disparan y el `dropoff.phone` que des
        puede recibir mensajes de WhatsApp, así que usa tu propio número. Una
        llave real (empieza con `lst_live_`) reserva y cobra envíos reales.

````

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