curl --request GET \
--url https://api.listo.delivery/v1/ledger \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.listo.delivery/v1/ledger"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.listo.delivery/v1/ledger', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.listo.delivery/v1/ledger",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.listo.delivery/v1/ledger"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.listo.delivery/v1/ledger")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.listo.delivery/v1/ledger")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "le_3kR7pM1xV9tB4nQ6cW2sZdYe",
"type": "provider_charge",
"amount_cents": -5800,
"delivery_id": "dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe",
"store_id": "st_4bR8kM2wQ7xT1nV5cZ9pLdYe",
"test": false,
"occurred_at": "2026-08-01T12:00:00Z"
}
],
"has_more": false,
"cursor": null
}{
"error": {
"code": "invalid_api_key",
"message": "The API key is missing, revoked, or not valid for this mode.",
"param": null,
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/invalid_api_key",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}{
"error": {
"code": "not_found",
"message": "No resource with that id exists for this API key.",
"param": null,
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/not_found",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}Lista tus movimientos del historial
Cada movimiento de tu saldo, los más nuevos primero, paginado por keyset. Es la superficie de conciliación: cierra un periodo recorriéndolo en lugar de sumar eventos de webhook, de modo que un evento que se te escapó, o uno que reenviaste, no cambia nada de lo que puedes reconstruir.
amount_cents tiene signo: los abonos son positivos y los cargos negativos, y por eso una página suma el movimiento de tu saldo a lo largo de ella. Lee la
dirección por ese signo y nunca por type.
type indica de dónde viene un movimiento. El par que conviene conocer es el
que produce un viaje desperdiciado:
provider_chargees la tarifa de envío cobrada por una cancelación tardía o un viaje de regreso, una tarifa por cada tramo recorrido, menos tu propio recargo como tienda. Es negativo y está ligado al pedido que lo causó. Este monto cambió el 2026-08-29: antes era lo que le costaba a Listo el viaje desperdiciado, así que espera valores más altos en los mismos pedidos desde esa fecha en adelante. La fila en sí no cambió.provider_charge_reversales ese cargo devuelto por completo, cuando resulta que el costo no se debía. Es positivo y se registra como una fila propia contra el mismodelivery_id, en lugar de como una reducción del cargo original, así que lo que te cobraron y lo que regresó siguen siendo legibles. Puede llegar semanas después del cargo. Empareja las dos pordelivery_idy espera montos iguales y opuestos.
Un tercer valor puede aparecer en el mismo pedido y es un monto distinto bajo otro encabezado:
order_value_reimbursementes el valor de un pedido que un repartidor recogió y que no llegó a nadie, abonado después de que lo reclamamos. Es positivo, ligado al pedido al que pertenece, y NO se empareja con ningún cargo en este historial: el valor del pedido nunca pasó por tu saldo, así que no hay nada que compensar. Se decide caso por caso y no está garantizado, así que nunca lo derives del total del pedido ni esperes uno por incidente.listo_charge_correctiones un cargo que Listo hizo por error y devolvió: unprovider_chargeque no te correspondía pagar. Es positivo, ligado al pedido que corrige, y se registra como una fila propia junto al cargo original, nunca como una reducción de este. Empárejalo con el cargo pordelivery_id; puede ser menor que el cargo, y un pedido puede tener más de uno. Se decide caso por caso, así que nunca lo derives de un cargo ni esperes uno por cada cargo.requote_adjustmentes la diferencia entre la tarifa que se le cobró al pedido y la tarifa que la red de repartidores registró para el viaje, cuando las dos no son iguales. Tiene signo: negativo cuando el viaje registrado costó más de lo que te cobramos, positivo cuando costó menos. Va ligado al pedido, se registra como una fila propia junto al cargo del pedido y nunca como una reescritura de este, y un reintento del mismo pedido lo ajusta al viaje finalmente registrado en lugar de sumar otro. No se empareja con ninguna otra fila, y es poco común.
Uno más es dinero que ganas, y es un abono:
developer_sharees lo que ganas por un viaje entregado de tu propia tienda, abonado cuando el viaje se entrega. Es positivo, y está ligado al pedido al que pertenece. Un viaje cancelado o devuelto no genera nada.
Otros dos son el Plan Listo de una tienda, pagados desde tu saldo:
plan_chargees un mes del plan de una tienda, con IVA incluido. Negativo. Un mes pagado con tarjeta no escribe fila, porque nunca tocó el saldo.plan_extra_chargeson las conversaciones del asistente de WhatsApp que pasan de lo que incluye el plan de una tiendafull, cobradas a ti junto con el plan de esa tienda. Negativo.
store_id indica a qué tienda pertenece un movimiento, en cada fila: la tienda del pedido, o la tienda cuyo plan se pagó. Es null cuando un movimiento no pertenece a una sola tienda, como una recarga o un retiro.
Este vocabulario CRECE. Pueden aparecer valores nuevos en cualquier versión, porque v1 cambia de forma aditiva y un movimiento que todavía no sabes nombrar sigue siendo dinero que se movió. Así que ramifica según los valores que conoces y deja que el resto pase de largo: cuenta un type desconocido dentro del saldo, muéstralo, y no lo descartes ni lo rechaces. Las filas que descartas son la diferencia entre tus registros contables y GET /v1/balance.
curl --request GET \
--url https://api.listo.delivery/v1/ledger \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.listo.delivery/v1/ledger"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.listo.delivery/v1/ledger', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.listo.delivery/v1/ledger",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.listo.delivery/v1/ledger"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.listo.delivery/v1/ledger")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.listo.delivery/v1/ledger")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "le_3kR7pM1xV9tB4nQ6cW2sZdYe",
"type": "provider_charge",
"amount_cents": -5800,
"delivery_id": "dlv_7hK2mQ9xR4tV6bN1cZ8sLwYe",
"store_id": "st_4bR8kM2wQ7xT1nV5cZ9pLdYe",
"test": false,
"occurred_at": "2026-08-01T12:00:00Z"
}
],
"has_more": false,
"cursor": null
}{
"error": {
"code": "invalid_api_key",
"message": "The API key is missing, revoked, or not valid for this mode.",
"param": null,
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/invalid_api_key",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}{
"error": {
"code": "not_found",
"message": "No resource with that id exists for this API key.",
"param": null,
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/not_found",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}Autorizaciones
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.
Respuesta
La solicitud fue exitosa.