curl --request POST \
--url https://api.listo.delivery/v1/connect_requests \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"owner_email": "rosa@lataqueria.mx",
"redirect_uri": "https://partner.example.com/listo/callback",
"state": "sess-8f21"
}
'import requests
url = "https://api.listo.delivery/v1/connect_requests"
payload = {
"owner_email": "rosa@lataqueria.mx",
"redirect_uri": "https://partner.example.com/listo/callback",
"state": "sess-8f21"
}
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({
owner_email: 'rosa@lataqueria.mx',
redirect_uri: 'https://partner.example.com/listo/callback',
state: 'sess-8f21'
})
};
fetch('https://api.listo.delivery/v1/connect_requests', 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/connect_requests",
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([
'owner_email' => 'rosa@lataqueria.mx',
'redirect_uri' => 'https://partner.example.com/listo/callback',
'state' => 'sess-8f21'
]),
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/connect_requests"
payload := strings.NewReader("{\n \"owner_email\": \"rosa@lataqueria.mx\",\n \"redirect_uri\": \"https://partner.example.com/listo/callback\",\n \"state\": \"sess-8f21\"\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/connect_requests")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"owner_email\": \"rosa@lataqueria.mx\",\n \"redirect_uri\": \"https://partner.example.com/listo/callback\",\n \"state\": \"sess-8f21\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.listo.delivery/v1/connect_requests")
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 \"owner_email\": \"rosa@lataqueria.mx\",\n \"redirect_uri\": \"https://partner.example.com/listo/callback\",\n \"state\": \"sess-8f21\"\n}"
response = http.request(request)
puts response.read_body{
"url": "https://admin.listo.delivery/connect/pcr_6mT4qX8vN2kR7bW1cZ5sLdYe",
"expires_at": "2026-08-10T21:07:29Z"
}{
"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": "No Listo account signs in with that email address. Create the store instead.",
"param": "owner_email",
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/validation_failed",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}Ask a restaurant to connect its account
Starts the flow that connects a restaurant’s EXISTING Listo account to you, and returns the link to put in front of its owner.
Send the owner’s email address and one of the redirect URIs registered for your account. We resolve the restaurant ourselves and tell you nothing about it: the response is a link and a deadline, and you learn which account you connected only if the owner approves.
Send them to url. They sign in with the Listo credentials they already
have, see who is asking and what it grants, and either approve or
cancel. We then send their browser back to your redirect_uri:
approved ?code=pcc_...&state=... exchange the code at
POST /v1/connections
cancelled ?error=access_denied&state=...
state is your own opaque value, echoed back untouched. Use it to tie
the browser that comes back to the session that left; a redirect with no
state you recognise is one you should not act on.
The link is good for 30 minutes. Minting another is free, so treat an expired one as a button to render again rather than an error to handle.
This endpoint is live-mode only. A test key gets 404: there is no test
version of a real restaurant’s real account.
curl --request POST \
--url https://api.listo.delivery/v1/connect_requests \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"owner_email": "rosa@lataqueria.mx",
"redirect_uri": "https://partner.example.com/listo/callback",
"state": "sess-8f21"
}
'import requests
url = "https://api.listo.delivery/v1/connect_requests"
payload = {
"owner_email": "rosa@lataqueria.mx",
"redirect_uri": "https://partner.example.com/listo/callback",
"state": "sess-8f21"
}
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({
owner_email: 'rosa@lataqueria.mx',
redirect_uri: 'https://partner.example.com/listo/callback',
state: 'sess-8f21'
})
};
fetch('https://api.listo.delivery/v1/connect_requests', 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/connect_requests",
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([
'owner_email' => 'rosa@lataqueria.mx',
'redirect_uri' => 'https://partner.example.com/listo/callback',
'state' => 'sess-8f21'
]),
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/connect_requests"
payload := strings.NewReader("{\n \"owner_email\": \"rosa@lataqueria.mx\",\n \"redirect_uri\": \"https://partner.example.com/listo/callback\",\n \"state\": \"sess-8f21\"\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/connect_requests")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"owner_email\": \"rosa@lataqueria.mx\",\n \"redirect_uri\": \"https://partner.example.com/listo/callback\",\n \"state\": \"sess-8f21\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.listo.delivery/v1/connect_requests")
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 \"owner_email\": \"rosa@lataqueria.mx\",\n \"redirect_uri\": \"https://partner.example.com/listo/callback\",\n \"state\": \"sess-8f21\"\n}"
response = http.request(request)
puts response.read_body{
"url": "https://admin.listo.delivery/connect/pcr_6mT4qX8vN2kR7bW1cZ5sLdYe",
"expires_at": "2026-08-10T21:07:29Z"
}{
"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": "No Listo account signs in with that email address. Create the store instead.",
"param": "owner_email",
"retryable": false,
"doc_url": "https://docs.listo.delivery/en/errors/validation_failed",
"request_id": "0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10"
}
}Authorizations
Your API key, sent as Authorization: Bearer <key>. Live and test keys are separate; the key you send decides which world the call acts in. To try calls, use a test key (it starts with lst_test_): no real courier is sent and no real money moves, but webhooks fire and the dropoff.phone you give can receive WhatsApp messages, so use your own number. A live key (it starts with lst_live_) books and charges real deliveries.
Body
The restaurant owner to ask, and where to send them back.