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

# Reintenta un despacho fallido

> Vuelve a despachar un envío que terminó en `no_courier`. Se vuelve a cotizar y a cobrar, porque es un despacho nuevo, pero el id del envío y tu `external_id` son estables entre reintentos: sigue siendo el mismo pedido.

Un reintento es un despacho nuevo, así que vuelve a consultar el Plan Listo de la tienda: una tienda real cuyo plan no está al corriente se rechaza con `402
plan_required`, igual que la creación.

Un reintento que la red de repartidores rechaza por distancia, en el límite mismo de la zona de servicio de 15 km de la tienda, responde `422 out_of_service_area`, el mismo código que dan la cotización y la creación para ese mismo hecho. Es definitivo: el envío se queda en `no_courier`, el intento queda registrado y no se cobra nada.




## OpenAPI

````yaml /openapi/es.yaml post /v1/deliveries/{delivery_id}/retries
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/deliveries/{delivery_id}/retries:
    post:
      tags:
        - Deliveries
      summary: Reintenta un despacho fallido
      description: >
        Vuelve a despachar un envío que terminó en `no_courier`. Se vuelve a
        cotizar y a cobrar, porque es un despacho nuevo, pero el id del envío y
        tu `external_id` son estables entre reintentos: sigue siendo el mismo
        pedido.


        Un reintento es un despacho nuevo, así que vuelve a consultar el Plan
        Listo de la tienda: una tienda real cuyo plan no está al corriente se
        rechaza con `402

        plan_required`, igual que la creación.


        Un reintento que la red de repartidores rechaza por distancia, en el
        límite mismo de la zona de servicio de 15 km de la tienda, responde `422
        out_of_service_area`, el mismo código que dan la cotización y la
        creación para ese mismo hecho. Es definitivo: el envío se queda en
        `no_courier`, el intento queda registrado y no se cobra nada.
      operationId: retryDelivery
      parameters:
        - name: delivery_id
          in: path
          required: true
          schema:
            type: string
          example: dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe
      responses:
        '200':
          description: La solicitud fue exitosa.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Delivery'
              example:
                id: dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe
                external_id: order-841
                code: LS-2001
                status: dispatching
                store_id: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
                test: false
                payment:
                  method: prepaid
                  status: unpaid
                  url: null
                  cash_to_collect_cents: null
                  paid_at: null
                order_value_cents: 2480
                fee_cents: 1200
                business_fee_cents: 0
                customer_fee_cents: 1200
                currency: MXN
                quote_id: qt_5nW3jP8vT2xQ6bM9cZ1sRdKe
                pickup:
                  name: Tortería La Mera Mera
                  street: Av. Juan Palomar y Arias 765, Zapopan, Jalisco 45110
                  lat: 20.691145
                  lng: -103.426885
                  notes: Toca el timbre del local, no la puerta principal
                dropoff:
                  name: Ana Torres
                  phone: '+528112345678'
                  street: Blvd. Puerta de Hierro 4965, Zapopan, Jalisco 45116
                  apt_unit: Depto 4B
                  lat: 20.709395
                  lng: -103.413393
                  notes: Ring buzzer 3
                  handoff: meet_at_door
                manifest:
                  items:
                    - name: Tacos al pastor
                      quantity: 2
                      size: small
                courier: null
                pickup_eta_at: '2026-07-29T13:49:01Z'
                dropoff_eta_at: '2026-07-29T14:19:21Z'
                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: null
                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
        '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
        '404':
          description: >-
            Ningún objeto con ese id existe para esta llave (`not_found`). Los
            objetos de otra cuenta son indistinguibles de los que nunca
            existieron.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              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
        '409':
          description: >-
            La solicitud entra en conflicto con el estado actual. El objeto ya
            no está en un estado que permita esto (`not_cancellable`,
            `not_reschedulable`, `not_retriable`, `not_editable`,
            `not_refundable`, `not_reactivatable`, `plan_ended`), la cancelación
            no se completó (`cancel_failed`), el cobro del plan aún está en el
            banco (`charge_in_progress`), la llave de idempotencia se reutilizó
            con otro cuerpo (`idempotency_conflict`, que ningún reintento
            corrige), la llave de idempotencia aún la retiene la solicitud que
            la envió primero (`idempotency_conflict`, y una vez que esa
            solicitud respondió, la misma llamada con la misma llave repite su
            respuesta), no hay URL de webhook configurada para el modo de la
            llave, así que no hay a dónde enviar
            (`webhook_endpoint_not_configured`), el evento es una posición del
            repartidor, que nunca se vuelve a enviar (`event_not_resendable`),
            el correo ya tiene una cuenta de Listo (`account_exists`), el punto
            de recolección de la tienda aún se está moviendo
            (`pickup_point_pending`), o ningún repartidor está libre para este
            viaje en este momento (`no_couriers_available`). Vale la pena
            repetirla sin cambios después de unos minutos: `cancel_failed`,
            `charge_in_progress`, `pickup_point_pending`,
            `no_couriers_available`, y una llamada rechazada porque su llave de
            idempotencia sigue retenida (repítela con la misma llave). Cualquier
            otro rechazo aquí requiere que primero cambien la solicitud o el
            objeto.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: not_retriable
                  message: Only a delivery left without a courier can be retried.
                  param: null
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/not_retriable
                  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: out_of_service_area
                  message: The dropoff address is outside the delivery service area.
                  param: null
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/out_of_service_area
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      security:
        - BearerAuth: []
components:
  schemas:
    Delivery:
      type: object
      description: >
        La forma de un envío: cada campo que puede llevar el objeto, con la

        nulabilidad que realmente tiene cada uno. Es la respuesta de cada
        operación

        que responde un envío (la creación, la consulta, una fila de lista, la

        reprogramación, la cancelación, el reintento y el reembolso) y el `data`
        de

        cada evento completo de envío, así que UN tipo sostiene todos ellos.


        Cada operación devuelve el objeto completo. Un campo que una llamada
        dada

        todavía no puede tener está presente y es null en lugar de faltar: una
        respuesta

        de creación tiene `courier` en null, porque todavía no se asigna ningún
        repartidor,

        y una reprogramación solo responde con un pedido que sigue programado,

        así que su `delivered_at` es null.


        El dinero va en centavos enteros, las marcas de tiempo son ISO-8601 en
        UTC, y cada

        enumeración de abajo es nuestra y no una copia de la de un tercero.
      properties:
        id:
          type: string
          description: El id del envío, con prefijo `dlv_`.
          example: dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe
        external_id:
          type:
            - string
            - 'null'
          description: >-
            Tu propia referencia del pedido, devuelta exactamente como la
            enviaste.
          example: order-841
        origin:
          type: string
          enum:
            - api
            - console
            - whatsapp
          description: >
            Qué vía creó el envío. `api` es uno que creaste con `POST
            /v1/deliveries`. `console` es uno que la tienda despachó desde la
            consola de comerciante de Listo, algo que una tienda solo puede
            hacer en el nivel de acceso `full`. `whatsapp` es uno que un cliente
            pidió a la tienda en un chat, y que la tienda luego aceptó; ningún
            operador lo escribió en ningún lado.


            Siempre está presente, nunca es null, y queda fijo durante toda la
            vida del envío. Un pedido de consola trae un `external_id` null,
            pero lee este campo en lugar de ese null: la referencia faltante es
            una consecuencia de la vía, no una forma de identificarla.
          example: api
        code:
          type: string
          description: >
            El código de pedido que citan el repartidor y nuestro soporte. Es
            único por

            tienda, no a nivel global, así que nunca identifiques un pedido solo
            por él.
          example: LS-2048
        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` y `canceled` son terminales. `no_courier` es
            un

            callejón sin salida que puedes reintentar o cancelar. Un envío
            `returned` es este

            mismo objeto de principio a fin; no hay un segundo envío para el
            tramo de

            regreso.


            `returning`: la entrega falló y el repartidor está llevando el

            pedido de regreso a la tienda; termina en `returned`. NO es
            terminal,

            y es donde un envío fallido ahora pasa los minutos entre el momento
            en que el

            repartidor se rinde en la puerta del cliente y el pedido llega de
            regreso. `returned` significa que el pedido ya regresó. Trata
            `returning`

            como un envío vivo: el repartidor sigue en movimiento y los eventos

            `delivery.courier_location` siguen llegando para él.


            El cargo de regreso se registra cuando el envío entra en
            `returning`,

            no cuando llega a `returned`, así que `charges` ya está completo en

            el evento `returning`.


            `awaiting_location`: un pedido que la tienda tomó por la consola de
            Listo

            o por su asistente de WhatsApp, retenido en espera de la ubicación
            de entrega. Todavía no

            se pide un repartidor. El cliente confirma la ubicación con un

            enlace de mapa o compartiéndola en el chat de WhatsApp, o la tienda

            la confirma desde su consola. Luego el envío pasa a

            `scheduled` o `dispatching`; a `no_courier` cuando no se puede pedir
            un repartidor

            para él (la ubicación está fuera de la zona donde entrega la tienda,
            o la tienda misma no puede pedir un repartidor

            en este momento); o a `canceled` si el pedido se cancela o la tienda

            lo cambia a recolección en el mostrador. Un envío que creas

            por esta API nunca pasa por ese estado.
        store_id:
          type: string
          description: El id `st_` de la tienda de donde recoge el repartidor.
        test:
          type: boolean
          description: >
            Si este pedido vive en el sandbox. Se deriva de la tienda, así que
            no hay un campo que fijar: una llave de prueba solo puede llegar a
            tiendas de prueba.
        payment:
          type: object
          description: Cómo paga el cliente y en qué estado va el pago.
          properties:
            method:
              type: string
              enum:
                - prepaid
                - cash
                - card
            status:
              type: string
              enum:
                - unpaid
                - awaiting_payment
                - paid
                - partially_refunded
                - refunded
            url:
              type:
                - string
                - 'null'
              format: uri
              description: >
                El enlace de pago, con marca propia, para un pedido con tarjeta
                que todavía espera

                pago. Listo se lo envía a tu cliente por sí mismo, por WhatsApp

                y por SMS cuando el WhatsApp no le llega, así que puedes
                mostrarlo o guardarlo pero no necesitas enviarlo. Es null en el
                momento en que el pedido se paga o se cancela, porque entonces
                el enlace ya está muerto.
            cash_to_collect_cents:
              type:
                - integer
                - 'null'
              description: >
                Lo que el repartidor cobra en la puerta: el valor de la
                mercancía más la parte de la tarifa que le toca al cliente. Es
                null, no cero, en cualquier pedido que no sea en efectivo. Cero
                es un valor real en un pedido en efectivo cuyo total en la
                puerta es cero (un pedido de valor `0` que cubres por completo);
                un pedido así se despacha sin cobro que hacer.
            paid_at:
              type:
                - string
                - 'null'
              format: date-time
          required:
            - method
            - status
            - url
            - cash_to_collect_cents
            - paid_at
        order_value_cents:
          type:
            - integer
            - 'null'
          description: El valor de la mercancía que enviaste, sin cambios.
        fee_cents:
          type: integer
          description: La tarifa total de envío.
        business_fee_cents:
          type: integer
          description: La parte de `fee_cents` que se te cobra a ti.
        customer_fee_cents:
          type: integer
          description: >
            La parte de `fee_cents` que se cobra al cliente. Siempre suma, junto
            con

            `business_fee_cents`, el total de `fee_cents`.
        tip_cents:
          type: integer
          description: >
            La propina al repartidor que enviaste en este envío, que se paga al
            repartidor y se

            pasa tal cual, al costo, sin margen. Deliberadamente FUERA de

            `fee_cents`, que es la tarifa de envío a la que suman
            `business_fee_cents` y

            `customer_fee_cents`. Es 0 cuando no enviaste ninguna, y nunca es
            null.
        currency:
          type: string
          enum:
            - MXN
        quote_id:
          type:
            - string
            - 'null'
          description: >
            La cotización `qt_` que vinculó este pedido, para que puedas ligar
            el precio que te

            cotizaron con el pedido que lo gastó. Es null cuando el pedido se
            cotizó en línea.
        pickup:
          type: object
          description: >
            Dónde recoge el repartidor, tomado como instantánea al crear, para
            que un pedido histórico siga reportando a dónde fue el repartidor de
            verdad aunque la tienda cambie de nombre o de lugar.
          properties:
            name:
              type: string
            street:
              type: string
            lat:
              type:
                - number
                - 'null'
              format: double
            lng:
              type:
                - number
                - 'null'
              format: double
            notes:
              type:
                - string
                - 'null'
              description: >-
                Las instrucciones permanentes de recolección de la tienda, no un
                valor por pedido.
          required:
            - name
            - street
            - lat
            - lng
            - notes
        dropoff:
          type: object
          description: A dónde entrega el repartidor, devuelto tal como lo enviaste.
          properties:
            name:
              type: string
            phone:
              type: string
              description: E.164, normalizado al recibirlo.
              example: '+528112345678'
            street:
              type: string
            apt_unit:
              type:
                - string
                - 'null'
            lat:
              type:
                - number
                - 'null'
              format: double
              description: >
                Null cuando la dirección nunca se geocodificó y el texto se
                juzgó en su lugar. Nunca un cero inventado.
            lng:
              type:
                - number
                - 'null'
              format: double
            notes:
              type:
                - string
                - 'null'
            handoff:
              type: string
              enum:
                - meet_at_door
                - leave_at_door
                - meet_outside
          required:
            - name
            - phone
            - street
            - apt_unit
            - lat
            - lng
            - notes
            - handoff
        manifest:
          type:
            - object
            - 'null'
          description: >
            La lista descriptiva de artículos que enviaste, para el repartidor.
            El dinero nunca

            sale de ella, con una excepción: un artículo de tamaño `xlarge` hace
            que el viaje sea en auto. Es null cuando no enviaste ninguna.
          properties:
            items:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  quantity:
                    type: integer
                  size:
                    type: string
                    enum:
                      - small
                      - medium
                      - large
                      - xlarge
                    description: >
                      Cómo se envió el artículo. `small` cuando no enviaste
                      ninguno, y en todos los artículos creados antes de que
                      existieran los tamaños, que es como se enviaron esos
                      artículos.
                required:
                  - name
                  - quantity
                  - size
          required:
            - items
        vehicle:
          type: string
          enum:
            - moto
            - car
          description: >
            Cómo se cotizó y despachó el viaje: `car` cuando pediste uno o
            cuando un artículo tenía tamaño `xlarge`, `moto` en los demás casos.
            El recargo de un auto va dentro de `fee_cents`. Siempre está
            presente, nunca es null, y queda fijo durante toda la vida del
            envío.


            No es el vehículo que llegó. Ese es `courier.vehicle_type`, que
            puede ser un auto en un viaje `moto` sin que la tarifa cambie.
          example: moto
        courier:
          type:
            - object
            - 'null'
          description: >
            Null hasta que se asigna un repartidor, y después CONGELADO: un
            envío terminal sigue reportando quién lo llevó.
          properties:
            name:
              type: string
            vehicle:
              type:
                - string
                - 'null'
              description: >
                En qué viaja el repartidor, en inglés sin importar el idioma de
                la solicitud ("Motorcycle", "Bicycle"). Se deriva de
                `vehicle_type`, así que es null exactamente cuando él lo es:
                muéstralo, y decide con `vehicle_type` en su lugar.
            vehicle_type:
              type:
                - string
                - 'null'
              description: >
                El tipo legible por máquina: `bicycle`, `car`, `van`, `truck`,

                `scooter`, `motorcycle` o `walker`. Null hasta que se asigna un
                vehículo al repartidor. Trata un valor que no aparece en la
                lista como desconocido y no como error: esta lista crece.
            plate:
              type:
                - string
                - 'null'
            phone:
              type:
                - object
                - 'null'
              description: >
                Un número enmascarado y el PIN de IVR, nunca un número personal.
                Marca

                `masked` e ingresa `pin` cuando el sistema lo pida. Null cuando
                no se ha

                asignado ningún número a este repartidor.
              properties:
                masked:
                  type: string
                pin:
                  type:
                    - string
                    - 'null'
              required:
                - masked
                - pin
            location:
              type:
                - object
                - 'null'
              description: >
                La posición en vivo, presente solo mientras el repartidor está
                realmente en

                movimiento y solo cuando ya hay una señal real de ubicación.
              properties:
                lat:
                  type: number
                  format: double
                lng:
                  type: number
                  format: double
                updated_at:
                  type:
                    - string
                    - 'null'
                  format: date-time
              required:
                - lat
                - lng
                - updated_at
            imminent:
              type: boolean
              description: >-
                El repartidor está a un minuto aproximadamente. Siempre es false
                una vez que termina el viaje.
          required:
            - name
            - vehicle
            - vehicle_type
            - plate
            - phone
            - location
            - imminent
        pickup_eta_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >
            Cuándo se espera que el repartidor llegue a la tienda. Es una hora
            absoluta, así que calcula tu propia cuenta regresiva; una cuenta
            regresiva calculada aquí ya está desactualizada cuando la lees.
        dropoff_eta_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >
            Cuándo se espera que el repartidor llegue con el cliente. Es null
            hasta que hay una estimación, y vuelve a ser null cuando la entrega
            fallida ya ocurrió (`returning`, `returned`): el repartidor entonces
            regresa el pedido a la tienda, no queda una llegada al cliente que
            describir, y aquí nunca se publica la hora en que vuelve a la
            tienda.
        tracking_url:
          type:
            - string
            - 'null'
          format: uri
          description: >
            La página de seguimiento en vivo, con marca propia, para el cliente:
            el mapa, el

            repartidor, el código de entrega y lo que se cobra en la puerta. Es
            null cuando no hay

            página a la que apuntar, así que nunca reenvías un enlace muerto.


            Hay página desde el momento en que el pedido se despacha, incluida

            toda la ventana antes de que se asigne un repartidor, y durante 48
            horas

            después de que el pedido se entrega, se cancela o regresa. Es null
            mientras

            un pedido sigue retenido esperando pago o esperando un pin de punto
            de entrega confirmado, y vuelve a ser null cuando pasan esas 48
            horas.
          example: https://app.listo.delivery/t/abc123
        verification:
          type:
            - object
            - 'null'
          description: >
            Está presente solo cuando el pedido trae un PIN de entrega: cuando
            lo pediste (`verification.dropoff_pincode` en la creación), o cuando
            Listo exige un PIN en cada envío de la cuenta de esta tienda, en
            cuyo caso está presente aunque no lo pediste. `pincode` es null
            hasta que el pedido se despacha de verdad, que es cuando se genera
            el código.
          properties:
            pincode:
              type:
                - string
                - 'null'
          required:
            - pincode
        return_verification:
          type: object
          description: >
            El código de devolución. Cuando un pedido no se puede entregar y el
            repartidor lo regresa a la tienda, el repartidor debe recibir este
            código en la tienda para entregar el pedido; sin él la devolución no
            se puede completar. `pincode` es una cadena de 4 dígitos mientras el
            envío está en `returning`, y null en cualquier otro estado (antes de
            la devolución, y una vez que está en `returned`). El objeto siempre
            está presente, así que este campo nunca necesita una verificación de
            presencia. Llega en `delivery.status_changed` (el paso a
            `returning`) y en `delivery.updated`, que traen el objeto completo.
            Un nuevo intento (un reintento) recibe su propio código. Muéstraselo
            a la tienda, y nunca al cliente.
          properties:
            pincode:
              type:
                - string
                - 'null'
              example: '4821'
          required:
            - pincode
        proof:
          type:
            - object
            - 'null'
          description: >
            Prueba de entrega. Null a menos que tengamos alguna, y cada campo es
            null cuando falta esa pieza; una foto sin firma es normal. Las URL
            son enlaces firmados propios de Listo hacia la copia guardada y son
            válidas durante 24 horas desde la respuesta (o el webhook) que las
            trajo: vuelve a pedir `GET /v1/deliveries/{id}` para obtener un
            enlace nuevo, o guarda una copia. **Una URL es null hasta que Listo
            tiene su propia copia de la imagen** (unos segundos después de que
            el repartidor la toma); la llegada de la copia se anuncia con un
            webhook `delivery.updated` que trae el enlace. Puede venir de un
            intento de entrega que falló en la puerta, así que por sí sola no
            prueba que el pedido se entregó: `proofs.dropoff.delivered` dice
            cuál. Prefiere `proofs`, que separa cada momento del viaje.
          properties:
            picture_url:
              type:
                - string
                - 'null'
              format: uri
            signature_image_url:
              type:
                - string
                - 'null'
              format: uri
            signature_name:
              type:
                - string
                - 'null'
            captured_at:
              type:
                - string
                - 'null'
              format: date-time
          required:
            - picture_url
            - signature_image_url
            - signature_name
            - captured_at
        proofs:
          type: object
          description: >
            La evidencia que dejó el viaje, una entrada por momento. Cada
            momento es

            null hasta que sucede (todavía no se capturó nada para él), así que

            este objeto siempre está presente con sus tres llaves. Un enlace de
            foto o de firma es el

            enlace firmado de Listo al archivo guardado, y **es null hasta que
            Listo tiene ese archivo** (unos segundos después de que el
            repartidor toma la foto): el resto del momento, quién firmó, la hora
            y la distancia, está desde el principio, y cuando el archivo llega
            un webhook `delivery.updated` trae el enlace. **Cada enlace es
            válido por 24 horas** desde la respuesta (o el webhook) que lo
            trajo: es un enlace firmado de vida corta, nunca uno permanente. Un
            socio que necesite la imagen después vuelve a pedir `GET
            /v1/deliveries/{id}` para obtener un enlace nuevo, o guarda una
            copia.
          properties:
            pickup:
              type:
                - object
                - 'null'
              description: >
                El repartidor que recoge el pedido en la tienda. Es null hasta
                que existe la

                foto de recolección.
              properties:
                photo_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                  description: Foto del pedido en la tienda.
                captured_at:
                  type:
                    - string
                    - 'null'
                  format: date-time
                  description: Cuándo vio Listo por primera vez esta foto.
              required:
                - photo_url
                - captured_at
            dropoff:
              type:
                - object
                - 'null'
              description: >
                El repartidor en la puerta del cliente. Está presente una vez
                que el pedido se entrega, o una vez que el intento dejó una
                foto, una firma o un

                firmante ahí, **incluido un intento que falló**: lee

                `delivered`, no la presencia de una foto, para saber si el
                pedido se entregó.
              properties:
                photo_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                  description: Foto tomada en la puerta.
                signature_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                  description: Imagen de la firma del destinatario.
                signer_name:
                  type:
                    - string
                    - 'null'
                  description: El nombre que escribió la persona que firmó.
                captured_at:
                  type:
                    - string
                    - 'null'
                  format: date-time
                  description: Cuándo vio Listo por primera vez esta evidencia.
                delivered:
                  type: boolean
                  description: >
                    True solo cuando el envío fue entregado. False significa que
                    la

                    evidencia viene de un intento que no entregó el pedido.
              required:
                - photo_url
                - signature_url
                - signer_name
                - captured_at
                - delivered
            return:
              type:
                - object
                - 'null'
              description: >
                El repartidor que regresa el pedido a la tienda después de una
                entrega fallida. Es null salvo que el pedido haya regresado con
                evidencia.
              properties:
                photo_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                  description: Foto del pedido devuelto en la tienda.
                signature_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                  description: Imagen de la firma de quien lo recibió en la tienda.
                signer_name:
                  type:
                    - string
                    - 'null'
                  description: El nombre que escribió la persona que firmó.
                captured_at:
                  type:
                    - string
                    - 'null'
                  format: date-time
                  description: Cuándo vio Listo por primera vez esta evidencia.
                meters_from_store:
                  type:
                    - integer
                    - 'null'
                  description: >
                    Qué tan lejos estaba el repartidor de la tienda cuando se
                    capturó esto, en metros enteros en línea recta. Null cuando
                    alguna de las dos posiciones es desconocida. Es evidencia
                    para que una persona la lea, no un veredicto.
              required:
                - photo_url
                - signature_url
                - signer_name
                - captured_at
                - meters_from_store
          required:
            - pickup
            - dropoff
            - return
        cancellation:
          type:
            - object
            - 'null'
          description: >-
            Quién canceló el pedido, y por qué. Es null en un pedido que no se
            canceló.
          properties:
            party:
              type: string
              enum:
                - partner
                - courier
                - platform
              description: >
                `partner` eres tú. `courier` es la persona que lo lleva.
                `platform` somos nosotros, y también cubre una cancelación
                reportada a nosotros en nombre del cliente final: el cliente
                final no es un actor en esta API, así que este campo nunca
                nombra a una persona que no puedas correlacionar.
            reason:
              type:
                - string
                - 'null'
              enum:
                - customer_canceled
                - store_closed
                - item_unavailable
                - wrong_address
                - no_longer_needed
                - other
                - null
              description: >
                El motivo que enviaste al cancelar. Es null cuando la atribución
                se derivó en lugar de darse, que es el caso de toda cancelación
                que tú no hiciste.
            description:
              type:
                - string
                - 'null'
          required:
            - party
            - reason
            - description
        failure_reason:
          type:
            - string
            - 'null'
          enum:
            - no_couriers_available
            - address_unreachable
            - customer_unavailable
            - delivery_failed
            - null
          description: >
            Por qué falló un despacho, agrupado según de qué lado está el
            problema, para que puedas actuar en lugar de solo reintentar a
            ciegas.

            `no_couriers_available` es el que se puede reintentar.

            `delivery_failed` es el cajón general honesto. Es null a menos que
            se haya registrado una falla.
        charges:
          type: array
          description: >
            Lo que se cobra por un viaje que se recorrió pero se desperdició: la
            tarifa de envío

            de cada tramo recorrido, menos tu propio margen de tienda. Vacío en

            casi todos los envíos, y nunca null.


            **Este monto cambió el 2026-08-29.** Antes era lo que le costaba a
            Listo el

            viaje desperdiciado, así que espera valores mayores en los mismos
            pedidos

            a partir de esa fecha. El campo en sí no cambió.


            Siempre está presente en un objeto de envío, así que trata un
            `charges`

            ausente como imposible y no como "sin cargos". Por esa razón está en
            la lista

            `required` de este esquema.


            Un cargo puede llegar en el `delivery.status_changed` final del
            envío o, cuando el

            costo se liquida después, en un `delivery.updated` posterior. No
            puedes suponer cuál,

            así que lee este campo en ambos: cobrar en el momento en que un
            envío llega a su

            estado terminal cobra de menos en cada pedido cuyo costo llegó
            tarde.


            El mismo movimiento aparece en `GET /v1/ledger` como una fila con
            signo, así que

            allí se lee negativo mientras que `amount_cents` aquí es positivo.
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - cancellation_fee
                  - return_fee
                description: >
                  Por qué se hizo el cargo. `cancellation_fee` es una
                  cancelación tardía, cuando un repartidor ya estaba trabajando
                  en el pedido. `return_fee` es un viaje de regreso después de
                  una entrega fallida, y se cobra como UNA sola entrada que
                  cubre los dos tramos del viaje en lugar de dos.
              amount_cents:
                type: integer
                description: >
                  Lo que te cobraron, positivo, en centavos de peso mexicano: la
                  tarifa de envío de cada tramo que se recorrió, menos tu propio
                  margen de tienda.
              charged_at:
                type: string
                format: date-time
                description: >
                  Cuándo se registró el cargo, que no es cuando terminó el
                  envío.
            required:
              - type
              - amount_cents
              - charged_at
        refunds:
          type: array
          description: >
            Cada vez que se devolvió dinero al cliente por este pedido, del más
            reciente al más antiguo. Vacío en un pedido que nunca se ha
            reembolsado, y nunca null.


            Siempre presente en un objeto de envío, así que trata un `refunds`
            ausente como algo imposible, no como "sin reembolsos". Está en la
            lista `required` de este esquema por esa razón, igual que `charges`
            arriba.


            Es una LISTA porque los reembolsos se repiten: un reembolso parcial
            va seguido de otro reembolso, cada uno con su propio monto, su
            propio motivo y su propio resultado. Aquí aparece todo reembolso que
            podemos ver, sin importar quién lo hizo, y por eso la lista vale la
            pena para conciliar. La cuenta conectada pertenece al comercio de
            registro (el titular del cargo), que puede reembolsar un cargo en
            ella sin pasar por nosotros; esos llegan como `requested_via:
            external`, y una lista que mostrara solo los reembolsos que tú
            pediste contradiría en silencio el `payment.status` de este pedido.


            Un reembolso viaja en `delivery.updated`, nunca en un evento propio.
          items:
            type: object
            properties:
              id:
                type: string
                description: El id del reembolso, con prefijo `rfn_`.
                example: rfn_4bQ7wS2xK9mT6vN3cZ8pLdYe
              amount_cents:
                type:
                  - integer
                  - 'null'
                description: >
                  Lo que volvió, positivo, en centavos de peso mexicano. NULL Y
                  NO CERO mientras un reembolso del saldo restante sigue en
                  curso: no lleva monto hasta que el reembolso se confirma, y un
                  cero diría que un reembolso no movió nada. Lee
                  `refunded_cents` más abajo en lugar de sumar este arreglo, que
                  es justo la razón por la que se publican los dos totales.
              reason:
                type:
                  - string
                  - 'null'
                description: |
                  El texto libre que escribió quien pidió el reembolso, o null.
                  Tus propias palabras cuando tú las enviaste; nunca es un enum.
              status:
                type: string
                enum:
                  - pending
                  - succeeded
                  - failed
                description: >
                  `pending` NO ES UNA FALLA y no debe reintentarse: un reembolso
                  de cargo directo queda retenido mientras el saldo de la cuenta
                  está corto y se aplica cuando llegan los fondos. Pasa a
                  `succeeded` por sí solo y lo publica como `delivery.updated`.
                  `failed` significa que no se movió nada.
              requested_via:
                type: string
                enum:
                  - api
                  - console
                  - automatic
                  - external
                description: >
                  Qué vía emitió el reembolso. `api` es tu propio `POST
                  /v1/deliveries/{id}/refunds`. `console` es el comerciante que
                  lo presiona en la consola de Listo. `automatic` es uno que
                  emitimos sin que nadie lo pidiera, en una cancelación o una
                  devolución. `external` es uno hecho en el panel de pagos
                  propio del comerciante, del que nos enteramos después.
              party:
                type: string
                enum:
                  - business
                  - platform
                description: >
                  De quién fue la decisión. `business` es el comerciante, o tú
                  actuando por tu tienda. `platform` es Listo emitiéndolo porque
                  el pedido terminó sin llegar al cliente.
              created_at:
                type: string
                format: date-time
                description: Cuándo se pidió el reembolso.
              succeeded_at:
                type:
                  - string
                  - 'null'
                format: date-time
                description: >
                  Cuándo se confirmó que el dinero iba en camino. Es null en un
                  reembolso pendiente o fallido.
            required:
              - id
              - amount_cents
              - reason
              - status
              - requested_via
              - party
              - created_at
              - succeeded_at
        refunded_cents:
          type: integer
          description: >
            La suma de los reembolsos EXITOSOS de arriba, en centavos de peso
            mexicano. 0,

            nunca null, en un pedido al que nada se le haya devuelto. Un
            reembolso `pending` no se cuenta, porque el dinero no se ha movido y
            todavía puede ser rechazado.


            Se publica en lugar de dejártelo calcular, porque el arreglo no
            siempre se puede sumar bien: un reembolso del saldo restante no
            lleva `amount_cents` hasta que se confirma, así que sumar la lista
            lo cuenta como cero y hace que todo el cargo parezca todavía
            reembolsable. En cambio tomamos ese caso de forma pesimista, como si
            hubiera consumido todo el saldo restante, porque adivinar a la baja
            termina reembolsando dos veces a un cliente.
        refundable_cents:
          type: integer
          description: >
            Lo que todavía podría devolver un reembolso adicional: lo que pagó
            el cliente (`order_value_cents` más `customer_fee_cents`, que ES el
            cargo) menos `refunded_cents`, sin bajar de cero. 0, nunca null.


            0 en cualquier pedido sin un pago con tarjeta cobrado detrás, sin
            importar su valor. El efectivo nunca tocó nuestros rieles de pago,
            el cliente de un pedido prepagado es tuyo y nosotros no tenemos nada
            de él, y un pedido que todavía espera pago nunca se cobró, así que
            en ninguno de estos hay algo que un reembolso pudiera devolver.


            Este es el techo contra el que `POST /v1/deliveries/{id}/refunds`
            rechaza un `amount_cents`, así que léelo antes de llamar en lugar de
            descubrir el límite a partir de un `refund_amount_too_large`.
        support_reference:
          type:
            - string
            - 'null'
          description: >
            Un identificador corto que citas cuando nos escalas un pedido. Null
            hasta

            que el pedido llega a despacho.
          example: 2080F
        pickup_ready_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            La hora de recolección programada que pediste. Es null en un pedido
            de despacho inmediato.
        requested_at:
          type:
            - string
            - 'null'
          format: date-time
        courier_assigned_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >
            Cuándo se observó por primera vez a un repartidor en el intento
            actual. En realidad es una observación y no un reloj de asignación,
            porque no se nos publica ningún momento de asignación. Es null en un
            intento que nunca lo tuvo.
        picked_up_at:
          type:
            - string
            - 'null'
          format: date-time
        delivered_at:
          type:
            - string
            - 'null'
          format: date-time
        canceled_at:
          type:
            - string
            - 'null'
          format: date-time
        returned_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-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
    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.