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

# Depurar tu integración

> Ve cada llamada que hiciste, cada evento que Listo emitió y cada intento de entregarlo, y reenvía un evento o envía uno de prueba.

Cuando algo no funciona, la pregunta es siempre una de tres. ¿Qué envió mi
llamada y qué respondió Listo? ¿Listo emitió el evento que yo esperaba? ¿Llegó a
mi endpoint, y qué contestó mi endpoint? Cada una tiene un registro que puedes
leer tú mismo, por la API o en el [panel de socios](https://partners.listo.delivery),
sin que nadie en Listo tenga que intervenir.

Todo pertenece al modo de la llave que uses: una llave de prueba lee registros de
prueba, una llave de producción lee los de producción, y ninguna ve los de la
otra. Los ids son los mismos que usa el resto de la API y son opacos: nunca
interpretes uno.

| Quieres saber | Lee | En el panel |
| - | - | - |
| Qué envió una llamada y qué respondió Listo | [`GET /v1/request_logs`](https://docs.listo.delivery/es/api-reference/request-logs/list-your-request-log) | Registro de solicitudes |
| Qué emitió Listo | [`GET /v1/events`](https://docs.listo.delivery/es/api-reference/events/list-your-webhook-events) | Webhooks |
| Si llegó a tu endpoint, y qué contestó | [`GET /v1/webhook_deliveries`](https://docs.listo.delivery/es/api-reference/webhook-deliveries/list-webhook-delivery-attempts) | Webhooks |

Toda lista tiene la misma forma que el resto de la API: un arreglo `data`, un
booleano `has_more` y un `cursor` opaco, de más reciente a más antiguo, hasta 50
filas por página.

## Registro de solicitudes

Cada llamada que tus llaves hacen a `/v1/` queda registrada: el método y la ruta,
el estatus con que respondimos, el `error.code` cuando falló, cuánto tardó, qué
llave la hizo (sus primeros caracteres, nunca el secreto) y si fue una llamada de
prueba. Cada fila trae un `request_id`, el valor del encabezado `X-Request-Id` de la
respuesta que recibiste, así que es lo que debes citar si alguna vez le escribes
a soporte.

Ese id es de Listo: toda respuesta lo trae en `X-Request-Id`, y todo
[error](https://docs.listo.delivery/es/errors.md) lo repite en el cuerpo como `error.request_id`, junto a un
`doc_url` que lleva a la página que explica el error. Si mandas tu propio
`X-Request-Id`, regresa en la respuesta (Listo limpia los caracteres que no acepta)
y es el id que se guarda, así que puedes cruzarlo con tus propios registros. El id
de un balanceador u otro servicio delante del nuestro no es este. Para encontrar
una llamada a partir de un id que copiaste de un error o de un registro, filtra la
lista con `request_id` (coincidencia exacta) o pégalo en el buscador de la página
Registro de solicitudes del panel.

Una llamada a una herramienta del servidor MCP escribe las mismas filas, y cada una
trae el `X-Request-Id` de la respuesta de `/mcp` que recibiste. Una herramienta de dos
pasos son dos peticiones a `/mcp` con dos ids. Una vista previa que llama a la API para leer tus datos
escribe una fila `GET` con el id de la vista previa; una que no llama a ningún endpoint
no escribe fila. La confirmación escribe la fila de la llamada que hizo el trabajo, y
confirmar el mismo token otra vez devuelve el primer resultado y no escribe fila.

```bash theme={null}
curl "https://api.listo.delivery/v1/request_logs?status=failed" \
  -H "Authorization: Bearer $LISTO_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "req_4f9c2b7e1a8d3056b1c2d3e4",
      "method": "POST",
      "path": "/v1/deliveries",
      "endpoint": "api/v1/deliveries#create",
      "status": 422,
      "error_code": "validation_failed",
      "duration_ms": 118,
      "request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10",
      "api_key_prefix": "lst_test_Ab1",
      "test": true,
      "created_at": "2026-11-06T16:02:11Z"
    }
  ],
  "has_more": false,
  "cursor": null
}
```

La lista se acota con `status` (`failed` es toda respuesta de 400 en adelante,
`succeeded` es el resto), `endpoint` (el valor exacto que muestra una fila, como
`api/v1/deliveries#create`), `request_id` (una sola llamada, por su id exacto), y
`created_after` y `created_before`, ambos instantes ISO-8601. Un `status` que no sea ninguno de los dos valores no coincide
con nada, y una fecha que no se pueda leer es un `422 validation_failed` que
nombra el parámetro.

[`GET /v1/request_logs/{id}`](https://docs.listo.delivery/es/api-reference/request-logs/retrieve-a-request-log-entry) agrega
los dos cuerpos, `request_body` y `response_body`, exactamente como se guardaron.
Esa es la respuesta a "por qué se rechazó esto": el cuerpo de la respuesta es el
sobre de error que recibiste, y el cuerpo de la solicitud es lo que realmente
enviaste, que muchas veces no es lo que querías enviar.

Qué se registra y qué no:

* **Las llamadas que identificaron una llave.** Una llamada sin llave, o con una
  equivocada o revocada, no pertenece a nadie y no está en tu registro, y tampoco
  una llamada rechazada por límite de frecuencia. Si estás seguro de haber hecho
  una llamada y no aparece, la razón es una de esas.
* **Las llaves y los enlaces de un solo uso se enmascaran.** Una llave de API en
  cualquier cuerpo aparece como sus primeros caracteres seguidos de `[key
  masked]`, y el enlace de la respuesta de `POST /v1/connect_requests` o de una
  llamada de onboarding de Stripe aparece como `[redacted: one-time link]`,
  porque tener ese enlace es la autoridad para usarlo. Lo que tú recibiste no se
  toca; solo se enmascara la copia guardada. Los encabezados nunca se guardan.
* **Un cuerpo se corta a 64 KB.** Uno más largo se guarda como un objeto con
  `_listo_truncated: true`, los `original_bytes` que tenía y los primeros 64 KB de
  su `text`, así que siempre se te avisa que no estás leyendo todo.
* **Las tres lecturas de depuración no guardan cuerpos.** Leer el registro de
  solicitudes, los eventos o las entregas de webhook también queda registrado,
  pero sin cuerpos: si no, el registro se copiaría a sí mismo cada vez que lo
  lees. Su `request_body` y `response_body` son `null`.
* **Las filas se conservan.** No hay ventana de retención contra la que correr.
  Una fila de antes de que existiera este registro no tiene `path` ni
  `request_id`, ambos `null`.

> [!WARNING]
>
> **Los cuerpos llevan datos de tus clientes**
>
> Los cuerpos de solicitud y respuesta de un envío contienen el nombre, el teléfono
> y la dirección que enviaste. Los ven las personas de tu equipo que pueden entrar
> al panel. Trata lo que copies de ahí como tratas los pedidos mismos.

## Eventos

[`GET /v1/events`](https://docs.listo.delivery/es/api-reference/events/list-your-webhook-events) lista lo que Listo
emitió para ti, lo haya recibido o no tu endpoint, y
[`GET /v1/events/{id}`](https://docs.listo.delivery/es/api-reference/events/retrieve-a-webhook-event) lee uno. Cada
elemento es exactamente el sobre de webhook que recibe tu endpoint (`id`, `type`,
`created_at`, `data`), así que puedes comparar lo que guardaste con lo que quisimos
decirte.

Se acota con `type` (un nombre publicado, como `delivery.status_changed`),
`delivery_id` (los eventos de un envío; un id que no es tuyo da `404`), y
`created_after` y `created_before`.

`delivery.courier_location` queda fuera a menos que lo pidas con `type`. Se emite
aproximadamente cada veinte segundos por cada viaje en camino y nunca se vuelve a
enviar, así que enterraría todo lo demás en la página.

## Entregas de webhook

Un evento es algo que Listo decidió contarte. Una **entrega de webhook** es un
intento de contártelo: la URL a la que hicimos `POST`, el estatus con que contestó
tu endpoint, los primeros 500 caracteres de su cuerpo, cuánto tardó y si lo
contamos como exitoso. [`GET /v1/webhook_deliveries`](https://docs.listo.delivery/es/api-reference/webhook-deliveries/list-webhook-delivery-attempts)
las lista.

```json theme={null}
{
  "id": "whd_8d2e5f1a7c3b9046a1b2c3d4",
  "event_id": "evt_9pQ2vX7mKdR4tYb1nZ3sLwCe",
  "event_type": "delivery.status_changed",
  "attempt": 2,
  "resent": false,
  "url": "https://example.com/listo/webhooks",
  "status_code": null,
  "error_message": "execution expired",
  "response_body": null,
  "duration_ms": 5000,
  "succeeded": false,
  "created_at": "2026-11-06T16:04:31Z"
}
```

`status_code` es `null` cuando nada contestó (un tiempo agotado, una conexión
rechazada, un error de certificado), y `error_message` dice cuál. `attempt` cuenta
los intentos del evento desde 1, incluidos los reintentos que Listo hace por su
cuenta cuando tu endpoint falla (ve [recibe y verifica webhooks](https://docs.listo.delivery/es/guides/webhooks.md#responde-y-después-trabaja)).
Se acota con `event_id` (un evento que no es tuyo da `404`), `status` (`failed` o
`succeeded`) y `created_after` y `created_before`.

El orden habitual de preguntas es: encuentra el evento en la lista, mira sus
intentos, y lee `status_code` y `response_body` para ver qué dijo tu endpoint. Un
`400` de tu propia verificación de firma, un `5xx`, un tiempo agotado y un evento
sin ningún intento (no había endpoint configurado para el modo) son cuatro
problemas distintos, y la fila del intento los distingue.

## Reenviar un evento

[`POST /v1/events/{id}/resend`](https://docs.listo.delivery/es/api-reference/events/send-a-webhook-event-again) envía un
evento a tu endpoint una vez más, sin cuerpo. Responde `202` con el sobre del
evento, y el envío ocurre enseguida: el intento nuevo aparece en
`GET /v1/webhook_deliveries` con `resent: true`, numerado después del último del
evento.

```bash theme={null}
curl -X POST "https://api.listo.delivery/v1/events/evt_9pQ2vX7mKdR4tYb1nZ3sLwCe/resend" \
  -H "Authorization: Bearer $LISTO_API_KEY"
```

Qué es un reenvío, para que puedas confiar en él:

* **El mismo evento.** Tiene el mismo `id` y un cuerpo idéntico byte por byte, así
  que un receptor que deduplica por `id`, como debe, lo trata como el evento que
  ya vio. Para ejercitar tu manejador otra vez, borra primero ese registro.
* **Firmado ahora.** La firma lleva la hora actual y el secreto vigente, así que
  verifica aunque el evento tenga días.
* **Enviado a la URL actual.** Si cambiaste tu endpoint desde que ocurrió el
  evento, el reenvío va al nuevo.
* **Una sola vez.** No hay reintentos detrás. Si tu endpoint falla, ves un intento
  fallido y tú decides qué sigue.
* **Con límite.** Un evento se puede reenviar 5 veces por hora. El sexto se
  rechaza con `429 resend_limit_reached`, que se puede reintentar pasada la hora.
  `Retry-After` dice cuántos segundos faltan para que puedas enviar de nuevo.
* **Nunca una posición de repartidor.** Un evento `delivery.courier_location` lo
  reemplaza el siguiente en segundos, así que pedir reenviar uno es
  `409 event_not_resendable`.
* **Necesita un endpoint.** Sin URL de webhook configurada para el modo de la
  llave, la respuesta es `409 webhook_endpoint_not_configured`.

> [!WARNING]
>
> **Un reenvío es una instantánea, no el estado actual**
>
> El cuerpo es la instantánea del momento en que ocurrió el evento. Reenviar un
> `delivery.status_changed` de hace un día entrega un estatus que el pedido dejó
> atrás hace mucho. Usa un reenvío para depurar tu endpoint o para recuperar un
> evento que sabes que perdiste. Para ponerte al día después de una caída, vuelve a
> leer los envíos con [`GET /v1/deliveries`](https://docs.listo.delivery/es/api-reference/deliveries/list-deliveries):
> eso siempre es la verdad actual. Ve [cuando te perdiste algunos](https://docs.listo.delivery/es/guides/webhooks.md#cuando-te-perdiste-algunos).

## Enviar un webhook de prueba

No necesitas un pedido para ejercitar tu endpoint.
[`POST /v1/test_webhooks`](https://docs.listo.delivery/es/api-reference/events/send-a-test-webhook) envía un
evento sintético de cualquier tipo publicado a la URL de webhook del modo de la
llave.

```bash theme={null}
curl -X POST "https://api.listo.delivery/v1/test_webhooks" \
  -H "Authorization: Bearer $LISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "delivery.status_changed"}'
```

Responde `202` con el evento tal como lo recibe tu endpoint, y el intento aparece
en `GET /v1/webhook_deliveries` como con cualquier otro evento:

```json theme={null}
{
  "id": "evt_3kM7qR2vX9pNdY4tB6nZ1sLw",
  "type": "delivery.status_changed",
  "created_at": "2026-11-06T16:10:02Z",
  "test": true,
  "data": {
    "id": "dlv_test_sample_000000000000",
    "external_id": "order-841",
    "status": "en_route"
  }
}
```

Cuatro cosas que debes saber de un evento de prueba. La primera te toca a ti: revisa `test` antes de actuar.

* **`"test": true` va en el sobre.** Un evento real no trae ese campo. Revísalo
  antes de hacer cualquier cosa 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 existe, así que
  `GET /v1/deliveries/{id}` con ese id responde `404`. Un manejador que vuelve a
  leer el objeto, como debe, no encontrará nada, y esa es la prueba de que es de
  prueba.
* **Funciona en los dos modos.** Una llave de prueba envía a tu endpoint de prueba
  y una llave de producción envía al de producción, así que puedes verificar un
  endpoint de producción sin hacer un pedido. Ese endpoint de producción sí recibe
  el evento de prueba, así que asegúrate de que ahí se ignore.
* **Tiene límite.** 30 eventos de prueba por hora por modo, y luego
  `429 test_webhook_limit_reached`, y `Retry-After` dice cuántos segundos faltan
  para que puedas enviar de nuevo. Un `type` que no es un nombre publicado es
  `422 validation_failed` con `param: "type"`, y un modo sin URL de webhook es
  `409 webhook_endpoint_not_configured`.

Un evento de prueba aparece en `GET /v1/events` como cualquier otro, con
`test: true`.

## Con qué rechazan estos endpoints

| HTTP | `code` | Qué pasó |
| - | - | - |
| 403 | `read_only_key` | Se pidió un reenvío o un webhook de prueba con una llave de solo lectura. Ambos son escrituras, así que una llave de solo lectura puede listar y leer, pero no enviar. Usa una llave emitida sin la opción de solo lectura. |
| 404 | `not_found` | El id no es tuyo, es del otro modo o nunca existió. Un filtro que nombra un evento o un envío que no es tuyo responde igual. |
| 422 | `validation_failed` | Un filtro de fecha no se pudo leer, o el `type` de un webhook de prueba no es un tipo de evento publicado. `error.param` lo nombra. |
| 409 | `webhook_endpoint_not_configured` | No hay URL de webhook para el modo de la llave, así que un reenvío o un webhook de prueba no tiene a dónde ir. Configura una en el panel. |
| 409 | `event_not_resendable` | El evento es un `delivery.courier_location`, que nunca se vuelve a enviar. |
| 429 | `resend_limit_reached` | Ese evento ya se reenvió 5 veces en la última hora. Reintenta cuando pase la hora. |
| 429 | `test_webhook_limit_reached` | Ya se enviaron 30 webhooks de prueba en este modo en la última hora. Reintenta más tarde. |

## En el panel

Los mismos registros están en el [panel de socios](https://partners.listo.delivery),
para el modo que tengas seleccionado.

* **Registro de solicitudes** lista tus llamadas con los mismos filtros, y cada
  llamada se abre para ver sus cuerpos de solicitud y respuesta.
* **Webhooks** guarda la configuración del endpoint y muestra cada intento.
  **Reenviar** está en cada intento de un evento que se puede reenviar, y
  **Enviar webhook de prueba** manda un evento sintético al endpoint de arriba.

Esos dos botones hacen lo que hacen los endpoints, con los mismos límites.

## Depura con tu asistente de IA

Si ya [conectaste un asistente de IA](https://docs.listo.delivery/es/guides/connect-ai-client.md), él mismo
puede leer los tres registros, así que puedes preguntarle en palabras sencillas:
"¿por qué falló mi última reserva?", "¿Listo envió el evento de entregado de ese
pedido?", "¿qué respondió mi endpoint?". Usa los mismos registros y los mismos
límites que los endpoints de arriba:

| Preguntas por | El asistente usa |
| - | - |
| Las llamadas que hiciste | `search_api_logs` y después `get_api_log` para los cuerpos de la petición y la respuesta |
| Lo que Listo emitió | `list_events` y después `get_event` |
| Si llegó a tu endpoint | `list_webhook_deliveries` |
| Enviarlo otra vez | `resend_event` |
| Probar tu endpoint sin un pedido | `send_test_webhook` |

`resend_event` y `send_test_webhook` actúan de inmediato: a diferencia de una
reserva o un retiro no mueven dinero, así que el asistente no te pide
confirmarlos. Conservan los límites descritos arriba, y el intento que provocan
se lee con `list_webhook_deliveries`. Cuando a un asistente se le indica esperar
por un límite, el error trae `retry_after_seconds`, los segundos que faltan para
que pueda volver a intentar.

## Una forma de trabajar

1. Reproduce el problema con una **llave de prueba**, para que nada real esté en
   juego.
2. Abre la llamada en el registro de solicitudes. El estatus, el `error.code` y el
   `message` del cuerpo de la respuesta, y tu propio cuerpo de solicitud,
   contestan la mayoría de las preguntas.
3. Si era un evento que esperabas, encuéntralo en `GET /v1/events`, y luego sus
   intentos en `GET /v1/webhook_deliveries`.
4. Arregla el manejador y luego **reenvía** ese evento, o envía un **webhook de
   prueba**, hasta que el intento diga `succeeded`.
5. Comprueba lo mismo en el endpoint de producción con un webhook de prueba antes
   de depender de él.


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