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

# Envía un webhook de prueba

> Envía un evento sintético de un `type` publicado a tu URL de webhook, para
probar un endpoint sin hacer un pedido. La respuesta es `202`
con el evento tal como lo recibe tu endpoint, y el intento aparece en
`GET /v1/webhook_deliveries` como cualquier otro.

- **`test: true` va en el sobre del evento.** Un evento real nunca trae el
  campo. Revísalo antes de hacer algo con consecuencias.
- **Sus ids no resuelven a nada.** `data` es una muestra realista del
  tipo, pero el envío, la tienda, el retiro o la cotización que nombra no
  existen, así que leerlo después responde `404`.
- **Cualquiera de los dos modos.** Una llave de prueba envía a tu endpoint de prueba y una llave real
  al tuyo real. El endpoint real sí recibe el evento de prueba.
- **Treinta por hora y por modo.** La trigésima primera es
  `429 test_webhook_limit_reached`, que se puede reintentar cuando pase la hora.
  `Retry-After` indica cuántos segundos faltan para que puedas volver a enviar.

Un `type` que no es un tipo de evento publicado es `422 validation_failed`
con `param: "type"`. Si no hay URL de webhook configurada para el modo de la llave,
la respuesta es `409 webhook_endpoint_not_configured`.




## OpenAPI

````yaml /openapi/es.yaml post /v1/test_webhooks
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/test_webhooks:
    post:
      tags:
        - Events
      summary: Envía un webhook de prueba
      description: >
        Envía un evento sintético de un `type` publicado a tu URL de webhook,
        para

        probar un endpoint sin hacer un pedido. La respuesta es `202`

        con el evento tal como lo recibe tu endpoint, y el intento aparece en

        `GET /v1/webhook_deliveries` como cualquier otro.


        - **`test: true` va en el sobre del evento.** Un evento real nunca trae
        el
          campo. Revísalo antes de hacer algo con consecuencias.
        - **Sus ids no resuelven a nada.** `data` es una muestra realista del
          tipo, pero el envío, la tienda, el retiro o la cotización que nombra no
          existen, así que leerlo después responde `404`.
        - **Cualquiera de los dos modos.** Una llave de prueba envía a tu
        endpoint de prueba y una llave real
          al tuyo real. El endpoint real sí recibe el evento de prueba.
        - **Treinta por hora y por modo.** La trigésima primera es
          `429 test_webhook_limit_reached`, que se puede reintentar cuando pase la hora.
          `Retry-After` indica cuántos segundos faltan para que puedas volver a enviar.

        Un `type` que no es un tipo de evento publicado es `422
        validation_failed`

        con `param: "type"`. Si no hay URL de webhook configurada para el modo
        de la llave,

        la respuesta es `409 webhook_endpoint_not_configured`.
      operationId: sendTestWebhook
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendTestWebhookRequest'
            example:
              type: delivery.status_changed
        required: true
      responses:
        '202':
          description: >-
            Aceptado. El evento de prueba es el que recibe tu endpoint, con
            `test: true` incluido (el campo `test` del evento). El envío queda
            en cola y ocurre justo después de esta respuesta; su resultado es un
            nuevo intento en `GET /v1/webhook_deliveries`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEvent'
              example:
                id: evt_9pQ2vX7mKdR4tYb1nZ3sLwCe
                type: delivery.status_changed
                created_at: '2026-11-06T08:49:43Z'
                data:
                  id: dlv_test_sample_000000000000
                  external_id: order-841
                  origin: api
                  code: LS-2048
                  status: courier_assigned
                  store_id: st_test_sample_00000000000000
                  test: false
                  payment:
                    method: prepaid
                    status: paid
                    url: null
                    cash_to_collect_cents: null
                    paid_at: '2026-07-28T18:41:03Z'
                  order_value_cents: 18000
                  fee_cents: 6900
                  business_fee_cents: 6900
                  customer_fee_cents: 0
                  tip_cents: 0
                  currency: MXN
                  quote_id: qt_test_sample_000000000000
                  pickup:
                    name: Test Sample Store
                    street: Av. Chapultepec 100, Guadalajara
                    lat: 20.6736
                    lng: -103.344
                    notes: null
                  dropoff:
                    name: Test Customer
                    phone: '+528112345678'
                    street: Av. Vallarta 200, Guadalajara
                    apt_unit: null
                    lat: 20.6766
                    lng: -103.3912
                    notes: null
                    handoff: meet_at_door
                  manifest:
                    items:
                      - name: Tacos
                        quantity: 2
                        size: small
                  vehicle: moto
                  courier:
                    name: Test Courier
                    vehicle: Test motorcycle
                    vehicle_type: moto
                    plate: TEST-000
                    phone:
                      masked: +52 81 **** 5678
                      pin: null
                    location:
                      lat: 20.6745
                      lng: -103.3501
                      updated_at: '2026-07-28T18:46:00Z'
                    imminent: false
                  pickup_eta_at: '2026-07-28T18:50:00Z'
                  dropoff_eta_at: '2026-07-28T19:10:00Z'
                  tracking_url: https://app.listo.delivery/t/test_sample_000000000000
                  verification:
                    pincode: '4821'
                  return_verification:
                    pincode: null
                  proof: null
                  proofs:
                    pickup: null
                    dropoff: null
                    return: null
                  cancellation: null
                  failure_reason: null
                  charges: []
                  refunds: []
                  refunded_cents: 0
                  refundable_cents: 0
                  support_reference: null
                  pickup_ready_at: null
                  requested_at: '2026-07-28T18:41:03Z'
                  courier_assigned_at: '2026-07-28T18:44:00Z'
                  picked_up_at: null
                  delivered_at: null
                  canceled_at: null
                  returned_at: null
                  created_at: '2026-07-28T18:41:03Z'
                  updated_at: '2026-07-28T18:46:00Z'
                test: true
        '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
        '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: webhook_endpoint_not_configured
                  message: >-
                    There is no webhook URL configured for this mode, so there
                    is nowhere to send it.
                  param: null
                  retryable: false
                  doc_url: >-
                    https://docs.listo.delivery/en/errors/webhook_endpoint_not_configured
                  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: That is not a webhook event type.
                  param: type
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/validation_failed
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
        '429':
          description: >-
            Demasiadas solicitudes (`rate_limited`). Espera un momento y vuelve
            a intentar. Un reenvío rechazado porque el evento ya se había
            reenviado 5 veces en la última hora es `resend_limit_reached`, y un
            webhook de prueba rechazado porque ya se habían enviado 30 en la
            última hora es `test_webhook_limit_reached`; en ambos casos vale la
            pena reintentarlo cuando haya pasado la hora.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: test_webhook_limit_reached
                  message: >-
                    30 test webhooks were already sent in the last hour. Try
                    again later.
                  param: null
                  retryable: true
                  doc_url: >-
                    https://docs.listo.delivery/en/errors/test_webhook_limit_reached
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      security:
        - BearerAuth: []
components:
  schemas:
    SendTestWebhookRequest:
      type: object
      description: De qué evento enviar una muestra.
      properties:
        type:
          type: string
          enum:
            - delivery.status_changed
            - delivery.updated
            - delivery.courier_location
            - withdrawal.updated
            - store.updated
            - store.connected
          description: El tipo de evento publicado del que enviar una muestra.
      required:
        - type
    WebhookEvent:
      description: >
        Cada evento que recibe tu endpoint de webhook, y cada evento que
        devuelven

        las lecturas de depuración: uno de los seis de abajo, distinguidos por
        `type`.

        `data` es el objeto al que se refiere el evento, y su forma depende de

        `type`. Consulta `WebhookEventEnvelope` para los campos que todos
        comparten y

        para saber cómo se comporta la entrega (al menos una vez, sin orden).
      oneOf:
        - $ref: '#/components/schemas/DeliveryStatusChangedEvent'
        - $ref: '#/components/schemas/DeliveryUpdatedEvent'
        - $ref: '#/components/schemas/DeliveryCourierLocationEvent'
        - $ref: '#/components/schemas/WithdrawalUpdatedEvent'
        - $ref: '#/components/schemas/StoreUpdatedEvent'
        - $ref: '#/components/schemas/StoreConnectedEvent'
      discriminator:
        propertyName: type
        mapping:
          delivery.status_changed: '#/components/schemas/DeliveryStatusChangedEvent'
          delivery.updated: '#/components/schemas/DeliveryUpdatedEvent'
          delivery.courier_location: '#/components/schemas/DeliveryCourierLocationEvent'
          withdrawal.updated: '#/components/schemas/WithdrawalUpdatedEvent'
          store.updated: '#/components/schemas/StoreUpdatedEvent'
          store.connected: '#/components/schemas/StoreConnectedEvent'
    ErrorResponse:
      type: object
      description: El cuerpo de toda respuesta 4xx y 5xx de esta API.
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error
    DeliveryStatusChangedEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEventEnvelope'
        - type: object
          properties:
            type:
              type: string
              description: Qué evento es este.
              enum:
                - delivery.status_changed
            data:
              $ref: '#/components/schemas/Delivery'
          required:
            - type
            - data
    DeliveryUpdatedEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEventEnvelope'
        - type: object
          properties:
            type:
              type: string
              description: Qué evento es este.
              enum:
                - delivery.updated
            data:
              $ref: '#/components/schemas/Delivery'
          required:
            - type
            - data
    DeliveryCourierLocationEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEventEnvelope'
        - type: object
          properties:
            type:
              type: string
              description: Qué evento es este.
              enum:
                - delivery.courier_location
            data:
              $ref: '#/components/schemas/CourierPing'
          required:
            - type
            - data
    WithdrawalUpdatedEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEventEnvelope'
        - type: object
          properties:
            type:
              type: string
              description: Qué evento es este.
              enum:
                - withdrawal.updated
            data:
              $ref: '#/components/schemas/Withdrawal'
          required:
            - type
            - data
    StoreUpdatedEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEventEnvelope'
        - type: object
          properties:
            type:
              type: string
              description: Qué evento es este.
              enum:
                - store.updated
            data:
              $ref: '#/components/schemas/Store'
          required:
            - type
            - data
    StoreConnectedEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEventEnvelope'
        - type: object
          properties:
            type:
              type: string
              description: Qué evento es este.
              enum:
                - store.connected
            data:
              $ref: '#/components/schemas/Store'
          required:
            - type
            - data
    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
    WebhookEventEnvelope:
      type: object
      description: >
        Los campos que comparten todos los eventos. La entrega es al menos una
        vez y sin orden

        por diseño: deduplica por `id`, y ordena por `created_at` o por las
        marcas de tiempo

        propias del objeto, no por orden de llegada. `type` y `data` los agrega

        cada evento, porque sus valores dependen de cuál evento sea.
      properties:
        id:
          type: string
          description: El id del evento, con prefijo `evt_`. Deduplica por este.
          example: evt_9pQ2vX7mKdR4tYb1nZ3sLwCe
        created_at:
          type: string
          format: date-time
          description: Cuándo se registró el evento, ISO-8601 en UTC.
          example: '2026-07-28T18:41:03Z'
        test:
          type: boolean
          description: >
            Presente, y en true, solo en un evento de prueba que pediste con

            `POST /v1/test_webhooks`. Un evento real no trae el campo en
            absoluto. El `data` de un evento de prueba es una muestra cuyos ids
            no corresponden a nada.
          example: true
      required:
        - id
        - created_at
    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
    CourierPing:
      type: object
      description: >
        El cuerpo reducido de un evento `delivery.courier_location`. Relaciónalo
        con un envío que ya tengas; no es el objeto del envío.
      properties:
        delivery_id:
          type: string
          description: El id `dlv_` del envío que lleva el repartidor.
        external_id:
          type:
            - string
            - 'null'
          description: >-
            Tu propia referencia de ese envío, para que no tengas que guardar la
            nuestra.
        location:
          type: object
          properties:
            lat:
              type: number
              format: double
            lng:
              type: number
              format: double
          required:
            - lat
            - lng
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >
            Cuándo se confirmó por última vez esta posición, que no es lo mismo
            que cuando se envió el evento.
      required:
        - delivery_id
        - external_id
        - location
        - updated_at
    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
    Store:
      type: object
      description: >
        Un punto de recolección. La disponibilidad para cobrar con tarjeta NO es
        un

        campo aquí a propósito: tiene endpoint propio, con detalle propio de
        requisitos pendientes.
      properties:
        id:
          type: string
          description: El id de la tienda, con prefijo `st_`.
          example: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
        external_id:
          type:
            - string
            - 'null'
          description: >
            Tu propia referencia de esta ubicación, si enviaste una. Es única
            por modo de llave, y se puede filtrar en la lista, para que puedas
            conciliar contra tu propio libro de tiendas sin guardar nuestros
            ids.
          example: pos-store-41
        name:
          type: string
          description: >
            El nombre que le diste a la tienda; para un restaurante que conectó
            una cuenta con varias sucursales, el negocio y la sucursal, como lo
            leen sus propios clientes (`Tortería La Mera Mera, Zapopan`). Una
            sucursal cuyo nombre ya empieza con el nombre del negocio, como
            `Tortería La Mera Mera Centro`, se publica tal cual. Cuando envías
            un nombre, un valor que empieza con el nombre de negocio de la
            tienda y una coma se lee como ese negocio más la sucursal, así que
            devolver el nombre que leíste no cambia nada.
        phone:
          type: string
          description: E.164, normalizado al recibirlo.
        street:
          type: string
        apt_unit:
          type:
            - string
            - 'null'
        lat:
          type:
            - number
            - 'null'
          format: double
        lng:
          type:
            - number
            - 'null'
          format: double
        pickup_notes:
          type:
            - string
            - 'null'
          description: Instrucciones permanentes para el repartidor que recoge aquí.
        timezone:
          type: string
          description: >
            La zona IANA en la que la tienda guarda sus horarios, normalizada a
            una

            sola escritura por zona. Se deriva de las coordenadas de la tienda,
            y

            se vuelve a derivar cada vez que la posición de la tienda se mueve.
            Una zona que tú envías

            se usa solo cuando la posición no puede responder (una tienda sin

            coordenadas, o una posición que ninguna zona reclama), y una que no
            coincide con la posición ni se guarda ni se trata

            como error.
          example: America/Mexico_City
        default_business_fee_share:
          type: integer
          minimum: 0
          maximum: 100
          description: >
            La parte de la tarifa de envío que esta tienda cubre por defecto,
            como porcentaje. El resto se cobra al cliente. Solo está vigente
            cuando `default_fee_split_mode` es `percent`; en cualquiera de los
            dos modos de monto, este es el porcentaje que la tienda conserva
            para cuando vuelva a ser uno.
        default_fee_split_mode:
          type: string
          enum:
            - percent
            - business_amount
            - customer_amount
          description: >
            Qué forma tiene el reparto predeterminado de esta tienda, y por eso
            cuál de los dos campos de arriba y de abajo está vigente. `percent`
            reparte la tarifa según `default_business_fee_share`;
            `business_amount` significa que la tienda cubre hasta
            `default_fee_split_amount_cents` de ella y el cliente paga el resto;
            `customer_amount` es el espejo, el cliente paga hasta ese monto y la
            tienda cubre el resto.


            Solo lectura. Lo fija el restaurante en su propia consola de Listo,
            y enviar `default_business_fee_share` al crear o actualizar devuelve
            la tienda a `percent`.
        default_fee_split_amount_cents:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >
            El monto entre el que dividen la tarifa los dos modos de monto, en
            centavos. Es null en

            modo `percent`, y nunca es null en ninguno de los dos modos de
            monto. Solo lectura.
        access_tier:
          type: string
          enum:
            - api_only
            - wallet
            - full
          description: >
            Cuánto de Listo usa la propia tienda. Los envíos de una tienda
            `api_only` gastan tu saldo, y los de una tienda `wallet` o `full`
            gastan el suyo; `billing` dice cuál. Se puede escribir al crear y al
            actualizar, y siempre es `api_only` en una tienda de prueba. Una
            tienda real de una cuenta cuyos restaurantes pagan su propio Plan
            Listo no puede ser `api_only`: no tiene consola desde la cual
            comprar un plan, así que una creación o una actualización que lo
            pida se rechaza con `422 validation_failed` y `param:
            "access_tier"`.
        markup_cents:
          type: integer
          minimum: 0
          description: >
            Tu propio margen sobre cada envío de esta tienda, en centavos. Se
            suma a la tarifa de envío, se reparte con el cliente como el resto
            de la tarifa, y se abona a tu saldo cuando el envío se entrega. Se
            puede escribir al crear y al actualizar.
        billing:
          type: string
          enum:
            - partner
            - store
          description: >
            De quién es el saldo que paga los envíos de esta tienda, leído de
            `access_tier` para que nunca tengas que derivarlo: `partner` es
            tuyo, `store` es el propio de la tienda. Solo lectura. No es quién
            paga el plan de la tienda, que es `plan.paid_by`.
        test:
          type: boolean
        created_at:
          type: string
          format: date-time
        plan:
          description: >
            El Plan Listo de la tienda, o `null` cuando la tienda no necesita
            plan: una tienda de prueba, o una tienda real creada antes de que
            existiera Plan Listo, que sigue pidiendo repartidores sin uno.
            También es `null` cuando la tienda paga su propio plan (cada tienda
            de una cuenta cuyas tiendas pagan su propio Plan Listo) y el
            restaurante todavía no ha comprado uno: el restaurante lo compra en
            su propia consola, y no tienes nada que pagar ni reactivar. También
            es `null` para una tienda de un socio con el esquema de precios sin
            Plan Listo, que no necesita plan. Cuando es un objeto, todas las
            llaves están presentes, y las que no aplican son `null`. Consulta
            [Plan y
            facturación](https://docs.listo.delivery/es/guides/plan-and-billing).
          type:
            - object
            - 'null'
          properties:
            status:
              type: string
              enum:
                - active
                - past_due
                - canceling
                - canceled
                - awaiting_payment
              description: >
                `active` está al corriente y se renueva en `next_charge_on`
                (null en un plan que está pasando al restaurante, ver
                `pending_paid_by`). `past_due` tiene un mes sin pagar, y sus
                envíos se rechazan con `402

                plan_required` hasta que el cobro pase. `awaiting_payment` es
                una tienda cuyo primer mes no pudieron cubrir ni tu saldo ni tu
                tarjeta: todavía no tiene plan, `price_cents` es el precio que
                se le cobrará antes de IVA (el monto necesario para pagar es ese
                más IVA), todas las fechas son null, sus envíos se rechazan con
                `402 plan_required`, y Listo le cobra por sí mismo en cuanto tu
                saldo o tu tarjeta lo cubran. `canceling` se canceló y se sirve
                hasta el día anterior a `ends_on`. `canceled` ya terminó, y se
                puede reiniciar.
            price_cents:
              type: integer
              description: >
                Lo que te cuesta un mes del plan de esta tienda, antes de IVA:
                tu precio, incluso mientras un restaurante que se conectó
                contigo sigue pagando su propio plan. Cuando `paid_by` es
                `store` porque la tienda paga su propio plan, es el precio de un
                `interval` del plan propio del restaurante, que no te
                corresponde pagar.
              example: 199000
            currency:
              type: string
              example: MXN
            interval:
              type: string
              description: >
                Cuánto dura un periodo de este plan, y el ciclo del que
                `price_cents` es el precio. `month` para todo plan que pagas.
                Una tienda que paga su propio plan reporta el intervalo que
                compró su restaurante, así que `year` es un restaurante que pagó
                doce meses de una vez.
              example: month
            current_period_ends_on:
              type:
                - string
                - 'null'
              format: date
              description: >
                El último día del periodo actual, ya pagado salvo que `status`
                sea `past_due`.
            next_charge_on:
              type:
                - string
                - 'null'
              format: date
              description: >
                El día en que se te cobra lo siguiente: la siguiente renovación
                o, mientras un restaurante que se conectó contigo siga pagando
                su propio plan, tu primer cobro. Es null una vez que el plan
                está terminando o ya terminó, y es null mientras está
                `past_due`: el mes adeudado se cobra en el momento en que tu
                saldo o tu tarjeta guardada lo permitan, y se reintenta cada día
                hasta que se paga. También es null mientras `courtesy` sea true.
                Igualmente es null en un plan que tú pagas y que está pasando al
                restaurante (`pending_paid_by` es `store`): pagas el periodo en
                el que estás y nada después. Cuando `paid_by` es `store` porque
                la tienda paga su propio plan, es el día en que se renueva el
                plan propio del restaurante, no un cobro a ti.
            canceled_at:
              type:
                - string
                - 'null'
              format: date-time
              description: Cuándo cancelaste el plan.
            ends_on:
              type:
                - string
                - 'null'
              format: date
              description: >
                El día en que el plan deja de dar servicio a la tienda, que es
                el día después del último día pagado, o el día en que se canceló
                un plan cuyo cobro estaba pendiente. Es null salvo que el plan
                esté terminando o ya haya terminado.
            paid_by:
              type: string
              enum:
                - partner
                - store
              description: >
                Quién paga este plan: `partner`, que eres tú, o `store`, el
                propio restaurante. `store` es un restaurante que se conectó
                contigo y aún paga su propio plan, y cada tienda que paga su
                propio Plan Listo porque tu cuenta está configurada así; ninguna
                de ellas te toca pagar.
            pending_paid_by:
              type:
                - string
                - 'null'
              description: >
                Quién lo pagará a partir de `pending_paid_by_on`, cuando eso
                está por cambiar, o null. `partner` eres tú: un restaurante que
                se conectó contigo y aún paga su propio plan, hasta la
                renovación que pagas tú. `store` es el restaurante: un plan que
                pagas tú y que pasa al restaurante en su próxima renovación
                porque tu cuenta está configurada para que cada restaurante
                pague su propio plan. Pagas el periodo en el que estás y nada
                después de él, por eso `next_charge_on` es null.
            pending_paid_by_on:
              type:
                - string
                - 'null'
              format: date
              description: >
                La fecha en que entra el cambio (el día en que empieza el primer
                periodo del nuevo pagador), o null.
            courtesy:
              type: boolean
              description: >
                `courtesy` es true mientras Listo le perdona el plan a esta
                tienda: no se cobra nada y `next_charge_on` es null.
          required:
            - status
            - price_cents
            - currency
            - interval
            - current_period_ends_on
            - next_charge_on
            - canceled_at
            - ends_on
            - paid_by
            - pending_paid_by
            - pending_paid_by_on
            - courtesy
      required:
        - id
        - external_id
        - name
        - phone
        - street
        - apt_unit
        - lat
        - lng
        - pickup_notes
        - timezone
        - default_business_fee_share
        - default_fee_split_mode
        - default_fee_split_amount_cents
        - access_tier
        - markup_cents
        - billing
        - test
        - created_at
        - plan
  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.