> ## 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.
> - On Listo's self-serve pricing the stores your account created itself carry no Plan Listo: `plan` is `null`, there is no Plan Listo per store, and the account pays for each trip and for the add-ons it switches on (monthly ones each month, per-use ones each time they are used). A restaurant that connects to a self-serve account keeps its own plan and pays it itself. On any other account every live store carries one Plan Listo, charged to the partner, unless the agreement has each restaurant pay its own. 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 (a store created by a self-serve account is never refused for lack of a plan, but it can still be refused with `402 plan_required` when Listo has switched its couriers off, and then the message says to contact Listo and nothing you pay changes it); 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, ChatGPT) can act on a developer account at `https://api.listo.delivery/mcp`, an MCP server over Streamable HTTP. A person connects by signing in (OAuth: the client opens a browser, the person signs in, chooses the account and allows it, optionally read-only); a script sends `Authorization: Bearer <API key>` instead. A sign-in access token works on `/mcp` only, never on `/v1/`. 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.

# La línea de comandos listo

> Inicia sesión desde tu terminal, recibe tus webhooks en tu propia computadora, manda eventos de prueba, sigue tus llamadas a la API y crea envíos, en modo de prueba por defecto.

`listo` es una herramienta de línea de comandos para la API de Listo Delivery.
Hace cinco cosas que de otra forma tendrías que armar tú:

* **Iniciar sesión** sin copiar una llave a ningún lado.
* **Recibir tus webhooks en tu propia computadora**, firmados como los firma
  producción, sin dirección pública y sin túnel.
* **Mandar un evento de prueba** de cualquier tipo.
* **Seguir tus llamadas a la API** en cuanto ocurren.
* **Crear, consultar y cancelar envíos**, y reproducir un escenario de
  repartidor en uno de prueba.

Todo corre en **modo de prueba** a menos que agregues `--live`. El modo de prueba
usa tu credencial de prueba y no mueve dinero (ve [modo de prueba](https://docs.listo.delivery/es/concepts/test-mode.md)).
El modo real necesita `--live`, y cada comando que escribe en modo real te
muestra exactamente lo que está a punto de hacer y te pregunta antes.

## Instálala

Elige una. Las tres instalan el mismo programa.

```bash npm theme={null}
npm install -g listo-cli
```

```bash Homebrew theme={null}
brew install listo-delivery/tap/listo
```

```bash Script de instalación theme={null}
curl -fsSL https://cli.listo.delivery/install.sh | sh
```

El script de instalación funciona en macOS y Linux (Intel y ARM). Deja `listo` en
`$HOME/.listo/bin` (define `LISTO_INSTALL_DIR` para elegir otra carpeta), imprime
la línea que debes agregar a tu `PATH` si esa carpeta no está en él, y no instala
nada a menos que antes pasen dos revisiones: la firma del archivo de sumas de
verificación de la versión, hecha con la llave de firma de Listo, y la suma de
verificación de la descarga. En Windows usa npm.

Comprueba que funciona:

```bash theme={null}
listo version
```

```
listo 0.1.0 (darwin/arm64)
```

Cada comando tiene `--help` con un ejemplo. Para completar comandos y opciones en
tu shell, carga una sola vez desde el perfil de tu shell el script que imprime
`listo completion` para bash, zsh, fish o PowerShell:

```bash theme={null}
listo completion zsh > "${fpath[1]}/_listo"
```

## Inicia sesión

```bash theme={null}
listo login
```

```
Tu código es ABCD-EFGH
Presiona Enter para abrir https://partners.listo.delivery/cli/confirm?code=ABCD-EFGH en tu navegador (o ábrela tú). Compara el código de la página con el de arriba antes de autorizar.
Esperando la aprobación...
Listo. Esta computadora es "MacBook de Ana" en modo de prueba. Corre listo login --live para agregar el modo real.
Guardado en el almacén de credenciales de tu sistema.
```

**1. Compara el código**

Presiona Enter para abrir la página, o abre la dirección tú. La página muestra un
código. **Revisa que sea el mismo que imprimió tu terminal** antes de autorizar:
así sabes que la aprobación es para la terminal que tienes enfrente y no para la
de alguien más.

**2. Elige el modo y autoriza**

La página ofrece **Pruebas** (sin dinero real) y **Real** (dinero real). El modo
real está disponible cuando tu saldo real tiene dinero o ya guardaste una tarjeta,
y aprobarlo pide tu contraseña. Presiona **Rechazar** si la solicitud no es tuya.

**3. Regresa a la terminal**

La terminal termina sola. La computadora ya aparece en el panel de socios, en
**Llaves de API** y luego **Apps conectadas**, como **Listo CLI en MacBook de
Ana**, donde **Desconectar** termina su acceso de inmediato.

**Una aprobación es un modo.** `listo login` inicia sesión en esta computadora en
modo de prueba. `listo login --live` pide el modo real de la misma forma y lo
agrega; las dos sesiones se guardan por separado, y `--live` en cualquier comando
posterior elige la real. Si la página aprueba un modo distinto del que pediste, la
terminal te lo dice.

Dónde se guarda la sesión: en el almacén de credenciales de tu sistema operativo
(el Llavero de macOS, el Administrador de credenciales de Windows o el Secret
Service en Linux). Cuando ningún almacén responde, como en un contenedor, usa un
archivo que solo tú puedes leer, y la última línea de la salida dice cuál usó.
`LISTO_NO_KEYRING=1` fuerza el archivo.

`listo whoami` le pregunta a la API a qué empresa y en qué modo pertenece la
credencial, y `listo logout` termina la sesión de un modo (`listo logout --live`
para el modo real): la sesión se revoca en Listo y se borra de esta computadora.

### Con una llave de API

¿Ya tienes una llave del panel? Guárdala, o defínela solo para una shell:

```bash theme={null}
listo login --key lst_test_...
```

```bash theme={null}
export LISTO_API_KEY=lst_test_...
listo whoami
```

`LISTO_API_KEY` gana sobre una sesión guardada. Una llave de prueba
(`lst_test_...`) funciona sin `--live`, y una llave real (`lst_live_...`) solo
con él; mezclarlas se rechaza antes de cualquier llamada, con la opción que debes
agregar o quitar.

### Sin terminal

En un script, un contenedor o un trabajo de CI no hay nadie que presione Enter.
Pide el código y termina el inicio de sesión desde otra shell:

```bash theme={null}
listo login --non-interactive
```

```json theme={null}
{
  "verification_url": "https://partners.listo.delivery/cli/confirm?code=ABCD-EFGH",
  "user_code": "ABCD-EFGH",
  "expires_in": 600,
  "next_step": "listo login --complete lso_dc_..."
}
```

Abre `verification_url` tú, compara el código y autoriza. Luego corre el comando
de `next_step` en la máquina que lo pidió, y guarda la sesión:

```bash theme={null}
listo login --complete lso_dc_...
```

`login` sin terminal hace lo mismo que `--non-interactive`. El código vence, y la
terminal lo dice ("El código venció antes de que lo aprobaran") y nombra el
comando que debes correr otra vez. `--key`, `--non-interactive` y `--complete` no
se pueden combinar.

## Recibe tus webhooks en tu computadora

```bash theme={null}
listo listen --forward-to http://localhost:3000/webhooks/listo
```

```
Listo [TEST]. Tu secreto de firma del webhook es whsec_... (ponlo como LISTO_WEBHOOK_SECRET en tu app)
12:04:11  delivery.status_changed  evt_3kM7qR2vX9pNdY4tB6nZ1sLw  -> 200 en 41 ms
12:04:13  delivery.courier_location  evt_9aB4cD6eF8gH1jK3mN5pQ7rS  -> 200 en 12 ms
```

`listen` abre una conexión en vivo con tu cuenta y manda cada evento que Listo
emite para ella, en el modo elegido, a la dirección que diste. Una línea por
evento: la hora, el tipo, el id del evento, lo que respondió tu servidor y cuánto
tardó. Presiona Ctrl+C para detenerlo. Nada tiene que ser alcanzable desde
internet.

### Verifica igual que en producción

Cada evento se manda como un `POST` con el JSON del evento, byte por byte como lo
escribe el emisor de webhooks de Listo, con un encabezado `X-Listo-Signature`
(`t=...,v1=...`) y un `User-Agent` de `listo-cli/<versión>`. Así que el código que
lo verifica es el que vas a publicar: el verificador del SDK, sobre el cuerpo
crudo, con un secreto.

```typescript verify_webhook_signature.ts theme={null}
// Verifies the signature on an incoming Listo webhook before trusting a byte of
// it. Reads the raw request body on stdin, the way your web framework hands it
// to you, and refuses anything that does not check out.
//
//   npm i listo-delivery
//   LISTO_SIGNATURE='t=...,v1=...' LISTO_WEBHOOK_SECRETS=whsec_a,whsec_b \
//     node verify_webhook_signature.ts < body.json
//
import { readFileSync } from "node:fs";
import { Listo, ListoError } from "listo-delivery";

// The RAW bytes, before any JSON parsing. Parsing and re-serializing changes
// them (key order, escapes, whitespace) and the signature covers what was sent,
// not what your JSON library would have sent.
const payload = readFileSync(0);

try {
  // Checks the signature and the 300 second window in BOTH directions (the
  // replay this scheme exists to stop), then parses the body. `secrets` is a
  // LIST because a rotation has two secrets live at once: accept either while
  // you swap over, then drop the old one. No API key is needed.
  const event = Listo.webhooks.constructEvent(
    payload,
    process.env.LISTO_SIGNATURE,
    process.env.LISTO_WEBHOOK_SECRETS!.split(","),
  );

  // Events arrive at least once and in no guaranteed order, so treat `id` as the
  // thing you deduplicate on and the object's own timestamps as the thing you
  // order by. Answer 2xx quickly and do the work afterwards.
  console.log(`${event.type} ${event.id}`);

  switch (event.type) {
    case "delivery.status_changed":
    case "delivery.updated":
      console.log(`order ${event.data.external_id} is ${event.data.status}`);
      break;
    case "delivery.courier_location":
      console.log(`courier at ${event.data.location.lat},${event.data.location.lng}`);
      break;
    default:
      // A new type is an additive change, so ignore what you do not recognise
      // rather than failing on it.
      console.log("ignored");
  }
} catch (error) {
  // `invalid_signature` (a ListoSignatureError) and `invalid_payload` (a signed
  // body that is not a JSON object) are both a ListoError.
  if (!(error instanceof ListoError)) throw error;

  // Answer 400 and stop. Do not parse it, do not act on it, do not log it as an
  // event you received.
  console.error("signature rejected");
  process.exit(1);
}
```

```python verify_webhook_signature.py theme={null}
# Verifies the signature on an incoming Listo webhook before trusting a byte of
# it. Reads the raw request body on stdin, the way your web framework hands it
# to you, and refuses anything that does not check out.
#
#   pip install listo-delivery
#   LISTO_SIGNATURE='t=...,v1=...' LISTO_WEBHOOK_SECRETS=whsec_a,whsec_b \
#     python verify_webhook_signature.py < body.json
#
import os
import sys

from listo_delivery import ListoError, webhooks
from listo_delivery.models import (
    DeliveryCourierLocationEvent,
    DeliveryStatusChangedEvent,
    DeliveryUpdatedEvent,
)

# The RAW bytes, before any JSON parsing. Parsing and re-serializing changes
# them (key order, escapes, whitespace) and the signature covers what was sent,
# not what your JSON library would have sent.
payload = sys.stdin.buffer.read()

try:
    # Checks the signature and the 300 second window in BOTH directions (the
    # replay this scheme exists to stop), then parses the body. `secrets` is a
    # LIST because a rotation has two secrets live at once: accept either while
    # you swap over, then drop the old one. No API key is needed.
    event = webhooks.construct_event(
        payload,
        os.environ.get("LISTO_SIGNATURE"),
        os.environ["LISTO_WEBHOOK_SECRETS"].split(","),
    )
except ListoError:
    # `invalid_signature` (a ListoSignatureError) and `invalid_payload` (a signed
    # body the SDK cannot read) are both a ListoError. Answer 400 and stop. Do
    # not parse it, do not act on it, do not log it as an event you received.
    sys.exit("signature rejected")

# Events arrive at least once and in no guaranteed order, so treat `id` as the
# thing you deduplicate on and the object's own timestamps as the thing you
# order by. Answer 2xx quickly and do the work afterwards.
kind = event.type_
print(f"{kind} {event.id}")

match event:
    case DeliveryStatusChangedEvent() | DeliveryUpdatedEvent():
        print(f"order {event.data.external_id} is {event.data.status}")
    case DeliveryCourierLocationEvent():
        print(f"courier at {event.data.location.lat},{event.data.location.lng}")
    case _:
        # A new type is an additive change, so ignore what you do not recognise
        # rather than failing on it.
        print("ignored")
```

```ruby verify_webhook_signature.rb theme={null}
# Verifies the signature on an incoming Listo webhook before trusting a byte of
# it. Reads the raw request body on stdin, the way your web framework hands it
# to you, and refuses anything that does not check out.
#
#   gem install listo-delivery
#   LISTO_SIGNATURE='t=...,v1=...' LISTO_WEBHOOK_SECRETS=whsec_a,whsec_b \
#     ruby verify_webhook_signature.rb < body.json
#
require "listo_delivery"

# The RAW bytes, before any JSON parsing. Parsing and re-serializing changes
# them (key order, escapes, whitespace) and the signature covers what was sent,
# not what your JSON library would have sent.
payload = $stdin.read

begin
  # Checks the signature and the 300 second window in BOTH directions (the
  # replay this scheme exists to stop), then parses the body. `secrets` is a
  # LIST because a rotation has two secrets live at once: accept either while
  # you swap over, then drop the old one. No API key is needed.
  event = ListoDelivery::Webhooks.construct_event(
    payload,
    ENV.fetch("LISTO_SIGNATURE", nil),
    ENV.fetch("LISTO_WEBHOOK_SECRETS").split(",")
  )
rescue ListoDelivery::Error
  # `invalid_signature` (SignatureError) and `invalid_payload` (a signed body
  # that is not a JSON object) are both a ListoDelivery::Error. Answer 400 and
  # stop. Do not parse it, do not act on it, do not log it as an event you
  # received.
  abort("signature rejected")
end

# Events arrive at least once and in no guaranteed order, so treat `id` as the
# thing you deduplicate on and the object's own timestamps as the thing you
# order by. Answer 2xx quickly and do the work afterwards.
puts "#{event.type} #{event.id}"

case event.type
when "delivery.status_changed", "delivery.updated"
  puts "order #{event.data.external_id} is #{event.data.status}"
when "delivery.courier_location"
  puts "courier at #{event.data.location.lat},#{event.data.location.lng}"
else
  # A new type is an additive change, so ignore what you do not recognise
  # rather than failing on it.
  puts "ignored"
end
```

```ruby HTTP (Ruby) theme={null}
# Verifies the signature on an incoming Listo webhook before trusting a byte of
# it. Reads the raw request body on stdin, the way your web framework hands it
# to you, and refuses anything that does not check out.
#
#   LISTO_SIGNATURE='t=...,v1=...' LISTO_WEBHOOK_SECRETS=whsec_a,whsec_b \
#     ruby verify_webhook_signature.rb < body.json
#
require "json"
require "openssl"

# The window a signature stays good for, matching the one Listo signs against.
# It is what stops a request someone captured from being replayed at you later.
TOLERANCE_SECONDS = 300

# `secrets` is a LIST because a rotation has two secrets live at once: accept
# either while you swap over, then drop the old one.
def listo_signature_valid?(payload, header, secrets, now: Time.now)
  match = /\At=(\d+),v1=(\h+)\z/.match(header.to_s)
  return false if match.nil?

  timestamp = match[1].to_i
  signature = match[2]

  # Checked in BOTH directions. A timestamp far in the past is the replay this
  # scheme exists to stop; one far in the future is the same attack against a
  # clock nobody looked at.
  return false if (now.to_i - timestamp).abs > TOLERANCE_SECONDS

  secrets.any? do |secret|
    expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{payload}")
    OpenSSL.secure_compare(expected, signature)
  end
end

# The RAW bytes, before any JSON parsing. Parsing and re-serializing changes
# them (key order, escapes, whitespace) and the signature covers what was sent,
# not what your JSON library would have sent.
payload = $stdin.read
header = ENV.fetch("LISTO_SIGNATURE", nil)
secrets = ENV.fetch("LISTO_WEBHOOK_SECRETS").split(",")

unless listo_signature_valid?(payload, header, secrets)
  # Answer 400 and stop. Do not parse it, do not act on it, do not log it as an
  # event you received.
  abort("signature rejected")
end

event = JSON.parse(payload)

# Events arrive at least once and in no guaranteed order, so treat `id` as the
# thing you deduplicate on and the object's own timestamps as the thing you
# order by. Answer 2xx quickly and do the work afterwards.
puts "#{event["type"]} #{event["id"]}"

case event["type"]
when "delivery.status_changed", "delivery.updated"
  delivery = event["data"]
  puts "order #{delivery["external_id"]} is #{delivery["status"]}"
when "delivery.courier_location"
  location = event.dig("data", "location")
  puts "courier at #{location["lat"]},#{location["lng"]}"
else
  # A new type is an additive change, so ignore what you do not recognise
  # rather than failing on it.
  puts "ignored"
end
```

El secreto es el que imprimió `listen`. Pásalo donde ese ejemplo lee
`LISTO_WEBHOOK_SECRETS` (el ejemplo recibe una lista separada por comas, así que
un solo secreto es una lista de uno). Tres cosas que debes saber de él:

* **Se crea en tu computadora, para `listen`.** No es el secreto de tu endpoint
  del panel, y ese nunca sale de Listo. En desarrollo verificas con el secreto
  que imprimió `listen`; en producción, con el de tu endpoint. Leer ambos de la
  misma variable de entorno en tu app es toda la diferencia.
* **No cambia.** Se crea la primera vez y es el mismo en cada arranque, así que lo
  configuras una vez. `listo listen --print-secret` lo imprime y termina, que es lo
  que quieres en un script: `export LISTO_WEBHOOK_SECRET=$(listo listen --print-secret)`.
* **Prueba y real tienen cada uno el suyo**, y `listo logout` borra el secreto del
  modo que termina.

Ve [Recibe y verifica webhooks](https://docs.listo.delivery/es/guides/webhooks.md#verifica-antes-de-confiar)
para lo que revisa un verificador, y [firma](https://docs.listo.delivery/es/concepts/signing.md) para el esquema.

### Qué hace `listen` con tu respuesta

Manda cada evento una sola vez y nunca reintenta: un `500` se muestra en rojo
para que arregles tu manejador y vuelvas a disparar el evento. Una respuesta de
redirección se reporta como la respuesta que es, no se sigue. Un servidor que no
está corriendo es una línea que dice que no se pudo conectar, y `listen` sigue con
el siguiente evento. Una petición que tarda más de 30 segundos se abandona.

Para recibir solo algunos tipos, nómbralos:

```bash theme={null}
listo listen --forward-to http://localhost:3000/webhooks/listo --events delivery.status_changed,store.updated
```

Un nombre que no es un tipo de evento publicado se rechaza, y el error lista los
que sí lo son.

### Cuando se cae la conexión

`listen` se reconecta solo y te avisa ("Se perdió la conexión, reconectando en 2
s..."). Al volver, lee los eventos escritos mientras tanto de
[`GET /v1/events`](https://docs.listo.delivery/es/api-reference/events/list-your-webhook-events), a partir
del último evento que vio, y manda los que no recibiste, del más antiguo al más
nuevo, cada evento una vez ("Reconectado. Eventos que se perdieron sin conexión:
3."). El filtro `--events` también se aplica a ellos. Lo que **no** repite:

* **Nada de antes de que `listen` arrancara.** La primera conexión no se pone al
  día con nada; empieza desde ahora.
* **Posiciones del repartidor** (`delivery.courier_location`). La lista de eventos
  las deja fuera a menos que pidas ese tipo, y una posición tan vieja no sirve.
* **Más de 2,000 eventos a la vez.** Una caída de un día no encola un día de
  tráfico.

Si necesitas la verdad completa después de un hueco largo, lee los envíos mismos,
como explica [Recibe y verifica webhooks](https://docs.listo.delivery/es/guides/webhooks.md#cuando-te-perdiste-algunos).

Si desconectas la computadora en el panel, o cierras sesión en otro lado, el
`listen` que está corriendo se detiene con "La sesión terminó." y el comando para
iniciar sesión otra vez.

## Manda un evento de prueba

```bash theme={null}
listo trigger delivery.status_changed
```

```
Se envió evt_7hS2kQ9xR4tV6bN1cZ8sLwYe (test: true) a tus sesiones de listen. Agrega --deliver para enviarlo también por POST a la URL de tu webhook.
```

`trigger` registra un evento de ejemplo de ese tipo, marcado `test: true`, y se lo
entrega a las sesiones de `listen` de tu cuenta. No llama a la URL de tu webhook,
así que no necesita que haya una configurada. Los ocho tipos son
`delivery.status_changed`, `delivery.updated`, `delivery.courier_location`,
`withdrawal.updated`, `store.updated`, `store.connected`,
`delivery.message_skipped` y `delivery.rated`.

Agrega `--deliver` para además enviarlo por `POST` a la URL de webhook que
configuraste en el panel para el modo, como sale un evento real:

```bash theme={null}
listo trigger delivery.status_changed --deliver
```

```
Se envió evt_7hS2kQ9xR4tV6bN1cZ8sLwYe (test: true) a tus sesiones de listen y quedó en cola hacia la URL de tu webhook.
```

El intento aparece entonces en [`GET /v1/webhook_deliveries`](https://docs.listo.delivery/es/api-reference/webhook-deliveries/list-webhook-delivery-attempts).
Con `--deliver` y sin URL para el modo, la API responde
[`webhook_endpoint_not_configured`](https://docs.listo.delivery/es/errors/webhook_endpoint_not_configured), y
mandar más de 30 eventos de prueba por hora responde
[`test_webhook_limit_reached`](https://docs.listo.delivery/es/errors/test_webhook_limit_reached). Es la misma
llamada que [`POST /v1/test_webhooks`](https://docs.listo.delivery/es/guides/debugging#enviar-un-webhook-de-prueba).

## Sigue tus llamadas a la API

```bash theme={null}
listo logs tail
```

```
Siguiendo el registro de solicitudes [TEST]. Presiona Ctrl+C para detener.
12:04:09  POST    /v1/deliveries  201  412 ms  0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
12:04:30  POST    /v1/deliveries  422  118 ms  validation_failed  5d8e3f2a-6b10-4c53-9a41-0b1f6c9a7e2d
```

Una línea por cada llamada `/v1/` de tu cuenta, en cuanto se hace, la haga quien la
haga: la hora, el método, la ruta, el estado (verde, o rojo desde 400), cuánto
tardó, el código de error cuando falló y el id de la solicitud. Ese id es el que
debes citar a soporte, y el que buscas en
[`GET /v1/request_logs`](https://docs.listo.delivery/es/api-reference/request-logs/list-your-request-log).

```bash theme={null}
listo logs tail --status failed
listo logs tail --endpoint api/v1/deliveries#create
```

`--status failed` muestra solo las respuestas desde 400, `succeeded` las demás, y
`--endpoint` recibe el valor exacto que muestra una fila del registro. Las
lecturas que `listo` hace por su cuenta para ponerse al día después de perder la
conexión se ocultan, para que no llenen tu registro; agrega `--all` para verlas.
Se reconecta y se pone al día igual que `listen`.

## Crea y consulta envíos

```bash theme={null}
listo deliveries create --data @order.json
```

```
Idempotency-Key: 6f1c0f0e-2f1b-4d3e-9f6a-8b7c5d4e3a21
[TEST] Se creó dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe (LS-2048), estado dispatching.
Costo de envío $80.04 MXN pagado por el cliente.
Síguela en https://app.listo.delivery/t/abc123
Corre listo listen para verla en vivo.
```

`order.json` es el cuerpo de [`POST /v1/deliveries`](https://docs.listo.delivery/es/api-reference/deliveries/create-a-delivery)
tal cual, y la API lo valida: ve [Cotiza y crea un envío](https://docs.listo.delivery/es/guides/quote-and-create.md).
Pasa el JSON con `--data @order.json`, con `--data @-` o redirigiéndolo, o escríbelo
en la misma línea de `--data`. El comando genera una `Idempotency-Key` para la
llamada y la muestra, así que repetir la misma llamada nunca reserva un segundo
repartidor. Para mandar la tuya, usa `--idempotency-key`.

```bash theme={null}
listo deliveries get dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe
listo deliveries cancel dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe --reason store_closed
```

`cancel` recibe `--reason` (`customer_canceled`, `store_closed`, `item_unavailable`,
`wrong_address`, `no_longer_needed` u `other`, el predeterminado) y `--description`,
una frase de explicación. Cancelar cuando un repartidor ya estaba trabajando en la
orden se cobra como cargo por cancelación, igual que por la API.

### Reproduce un escenario de repartidor

```bash theme={null}
listo simulate returned
```

```
Empezó el escenario returned: dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe (dispatching).
Corre listo listen para verla en vivo.
```

`simulate` crea un envío de prueba con el destino de ejemplo de la guía de inicio
y le pide al repartidor simulado que reproduzca `returned`, `no_courier`,
`delayed_pickup` o `courier_reassigned` (ve [modo de prueba](https://docs.listo.delivery/es/concepts/test-mode.md)).
Usa tu primera tienda de prueba, o la que nombres con `--store st_...`. Con
`listo listen` corriendo en otra ventana ves los eventos que produce. Corre solo
en modo de prueba: una llave real se rechaza antes de llamar a nada.

## Modo real

Agrega `--live` y el comando actúa sobre tu cuenta real, con dinero real y
repartidores reales. Cada línea que nombra un modo muestra la marca `[LIVE]` en
lugar de `[TEST]`. Leer no tiene ceremonia (`whoami`, `deliveries get`, `listen`,
`logs tail`). **Escribir pregunta primero**, y la pregunta dice exactamente qué va
a pasar:

```bash theme={null}
listo deliveries create --live --data @order.json
```

```
Esto reservará un repartidor para "Taquería El Güero" (st_4bR8kM2wQ7xT1nV5cZ9pLdYe) hacia Av. Chapultepec 123, Guadalajara, costo de envío $89.00 MXN pagado por la tienda, de tu saldo real. ¿Continuar? [y/N]
```

Para armar esa frase, el comando primero cotiza el viaje con
[`POST /v1/quotes`](https://docs.listo.delivery/es/api-reference/quotes/price-a-delivery) para la misma tienda y
el mismo destino, y con `y` reserva **esa cotización**, así que el monto que leíste
es el monto que se cobra. Cualquier respuesta que no sea `y` cancela, y no se
cambia nada.

* `deliveries cancel --live` muestra el envío, su estado y lo que puede costar la
  cancelación antes de preguntar.
* `trigger --live` dice que el evento se registrará en tu cuenta real, y con
  `--deliver` que llegará a la URL de tu webhook real.
* `--yes` se salta la pregunta, para scripts en los que ya decidiste confiar. En un
  envío igual imprime la frase, para que el registro diga qué se hizo.
* Sin terminal donde preguntar (un script, una redirección) y sin `--yes`, el
  comando se rechaza con código de salida 2 y la línea `Agrega --yes.`. Nunca
  sigue por su cuenta.

## En scripts

La salida es JSON cuando agregas `--json` y siempre que stdout no es una terminal,
así que una redirección siempre recibe algo que un programa puede leer; el color
aparece solo en una terminal y `NO_COLOR` lo apaga.

```bash theme={null}
listo deliveries get dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe | jq -r .status
listo listen --forward-to http://localhost:3000/webhooks/listo --json
```

Un comando que imprime un objeto de la API imprime el objeto de la API mismo, en
una sola línea cuando se redirige. `listen` y `logs tail` imprimen un objeto JSON
por línea. Un error va a stderr como un objeto JSON (`code`, `message`, y `param`,
`doc_url`, `request_id` y `next_step` cuando los hay), y las líneas para personas
que dicen qué pasó (`Listo`, `Se envió`, la frase de confirmación) van también a
stderr, así que stdout no lleva nada más que JSON.

| Código de salida | Significado |
| - | - |
| `0` | Funcionó. |
| `1` | La API rechazó la llamada, o la operación falló. El mensaje, el código, el `doc_url` y el `request_id` son los de la API, y se nombra el siguiente paso ("Corre listo login"). |
| `2` | El comando se escribió mal, o una escritura en modo real no se pudo confirmar. |

## Idioma

La herramienta habla español o inglés. Usa el idioma que pidas con `--lang es` o
`--lang en`, si no `LISTO_LANG`, si no el de tu computadora (`LC_ALL`,
`LC_MESSAGES`, `LANG`, y en una Mac el idioma del sistema), y todo lo que empiece
con `es` es español. Los comandos, las opciones y las llaves del JSON se quedan en
inglés. El idioma también viaja a la API en cada llamada, así que el `message` de
un error llega en él.

## Variables de entorno

| Variable | Significado |
| - | - |
| `LISTO_API_KEY` | Una llave de API (`lst_test_...` o `lst_live_...`), que se usa en lugar de una sesión guardada. |
| `LISTO_API_URL` | La dirección de la API. Por defecto `https://api.listo.delivery`. |
| `LISTO_LOGIN_URL` | La dirección de inicio de sesión. Por defecto: la dirección de la API con `api` cambiado por `login`. |
| `LISTO_CABLE_URL` | La conexión en vivo que usan `listen` y `logs tail`. Por defecto `wss://cable.listo.delivery/cable`. |
| `LISTO_LANG` | Idioma de la salida, `es` o `en`. `--lang` gana sobre ella. |
| `LISTO_CONFIG_DIR` | Dónde guarda sus archivos la herramienta. Por defecto: la carpeta de configuración de tu sistema, dentro de `listo`. |
| `LISTO_NO_KEYRING` | Define `1` para guardar las credenciales en el archivo y nunca en el almacén del sistema. |
| `LISTO_NO_UPDATE_NOTIFIER` | Define `1` para silenciar el aviso diario de que hay una versión nueva. |
| `NO_COLOR` | Apaga el color. |

## Mantenla al día

Una vez al día, en una terminal, `listo` imprime una línea en stderr cuando existe
una versión más nueva, con el comando que la actualiza según cómo la instalaste.
Nunca se imprime con `--json` ni cuando la salida se redirige.

| Instalada con | Se actualiza con |
| - | - |
| npm | `npm i -g listo-cli` |
| Homebrew | `brew upgrade listo` |
| El script de instalación | `listo update` |

`listo update` reemplaza el programa mismo cuando lo instalaste con el script de
instalación, después de revisar la descarga contra la llave de firma de la versión
y su suma de verificación; no se reemplaza nada a menos que pasen las dos. Para una
copia de npm o de Homebrew imprime el comando de esa herramienta. `listo update --check` solo te dice si existe una versión más nueva. Nunca se actualiza en
segundo plano, para que la herramienta que estás usando no cambie debajo de ti a
mitad de una sesión.


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