curl --request POST \
--url https://api.listo.delivery/v1/stores/{store_id}/owner \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Rosa Delgado",
"email": "rosa@tacoslamera.mx"
}
'import requests
url = "https://api.listo.delivery/v1/stores/{store_id}/owner"
payload = {
"name": "Rosa Delgado",
"email": "rosa@tacoslamera.mx"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Rosa Delgado', email: 'rosa@tacoslamera.mx'})
};
fetch('https://api.listo.delivery/v1/stores/{store_id}/owner', 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/stores/{store_id}/owner",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Rosa Delgado',
'email' => 'rosa@tacoslamera.mx'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.listo.delivery/v1/stores/{store_id}/owner"
payload := strings.NewReader("{\n \"name\": \"Rosa Delgado\",\n \"email\": \"rosa@tacoslamera.mx\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.listo.delivery/v1/stores/{store_id}/owner")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Rosa Delgado\",\n \"email\": \"rosa@tacoslamera.mx\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.listo.delivery/v1/stores/{store_id}/owner")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Rosa Delgado\",\n \"email\": \"rosa@tacoslamera.mx\"\n}"
response = http.request(request)
puts response.read_body{
"email": "rosa@tacoslamera.mx",
"invited_at": "2026-08-01T22:17:10Z"
}{
"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": "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"
}
}{
"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"
}
}{
"error": {
"code": "validation_failed",
"message": "This store already has an owner (rosita@tacoslamera.mx). A store has one console login, and this endpoint cannot change who holds it.",
"param": "email",
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/validation_failed",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}Invita al dueño de una tienda
Le da a quien maneja la tienda un acceso a su consola de Listo. Nosotros creamos el acceso y le enviamos por correo un enlace para elegir su propia contraseña. La respuesta trae la dirección a la que se envió el enlace y cuándo se envió, y nunca una contraseña ni un token, así que no hay nada en ella que guardar.
Repetir la llamada es seguro. La misma dirección recibe un enlace nuevo en lugar de un segundo acceso, así que un reintento tras un timeout no causa daño, y también es la forma de volver a enviar el enlace cuando el primero ya venció. El enlace dura siete días.
Una dirección que ya tiene un acceso a Listo en otro lugar está bien. Esa persona recibe un acceso separado para esta tienda, con su propia contraseña, y la cuenta que ya tenía se queda como estaba. Alguien que maneja dos de tus locales puede usar la misma dirección para ambos.
Tres rechazos:
422 console_access_requireden una tienda cuyoaccess_tieresapi_only. Esa tienda no tiene consola a la que entrar, así que no hay a quién invitar. Define primero el nivel y luego vuelve a llamar a este endpoint.422 validation_failedconparam: "email"cuando la tienda ya tiene un dueño con una dirección distinta. Elmessagenombra a ese dueño. Una tienda tiene un solo dueño, y este endpoint no puede cambiar quién es. Para enviarle al dueño un enlace nuevo, llama con la dirección que nombra elmessage.422 validation_failedconparam: "email"cuando otra solicitud está configurando la misma dirección para esta tienda en ese mismo momento. Repite la llamada: para entonces la primera ya terminó, y la tuya vuelve a enviar el enlace.
curl --request POST \
--url https://api.listo.delivery/v1/stores/{store_id}/owner \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Rosa Delgado",
"email": "rosa@tacoslamera.mx"
}
'import requests
url = "https://api.listo.delivery/v1/stores/{store_id}/owner"
payload = {
"name": "Rosa Delgado",
"email": "rosa@tacoslamera.mx"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Rosa Delgado', email: 'rosa@tacoslamera.mx'})
};
fetch('https://api.listo.delivery/v1/stores/{store_id}/owner', 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/stores/{store_id}/owner",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Rosa Delgado',
'email' => 'rosa@tacoslamera.mx'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.listo.delivery/v1/stores/{store_id}/owner"
payload := strings.NewReader("{\n \"name\": \"Rosa Delgado\",\n \"email\": \"rosa@tacoslamera.mx\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.listo.delivery/v1/stores/{store_id}/owner")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Rosa Delgado\",\n \"email\": \"rosa@tacoslamera.mx\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.listo.delivery/v1/stores/{store_id}/owner")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Rosa Delgado\",\n \"email\": \"rosa@tacoslamera.mx\"\n}"
response = http.request(request)
puts response.read_body{
"email": "rosa@tacoslamera.mx",
"invited_at": "2026-08-01T22:17:10Z"
}{
"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": "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"
}
}{
"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"
}
}{
"error": {
"code": "validation_failed",
"message": "This store already has an owner (rosita@tacoslamera.mx). A store has one console login, and this endpoint cannot change who holds it.",
"param": "email",
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/validation_failed",
"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.