Cancel Pending Messages
curl --request POST \
--url https://app.tuco.ai/api/messages/cancel-for-lead \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"leadId": "<string>",
"recipientPhone": "<string>",
"recipientEmail": "<string>"
}
'import requests
url = "https://app.tuco.ai/api/messages/cancel-for-lead"
payload = {
"leadId": "<string>",
"recipientPhone": "<string>",
"recipientEmail": "<string>"
}
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({leadId: '<string>', recipientPhone: '<string>', recipientEmail: '<string>'})
};
fetch('https://app.tuco.ai/api/messages/cancel-for-lead', 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://app.tuco.ai/api/messages/cancel-for-lead",
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([
'leadId' => '<string>',
'recipientPhone' => '<string>',
'recipientEmail' => '<string>'
]),
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://app.tuco.ai/api/messages/cancel-for-lead"
payload := strings.NewReader("{\n \"leadId\": \"<string>\",\n \"recipientPhone\": \"<string>\",\n \"recipientEmail\": \"<string>\"\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://app.tuco.ai/api/messages/cancel-for-lead")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"leadId\": \"<string>\",\n \"recipientPhone\": \"<string>\",\n \"recipientEmail\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/messages/cancel-for-lead")
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 \"leadId\": \"<string>\",\n \"recipientPhone\": \"<string>\",\n \"recipientEmail\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"cancelledCount": 123,
"jobsRemovedFromQueue": 123,
"leadId": "<string>",
"messageIds": [
"<string>"
]
}Messages
Cancel Pending Messages
Cancel all pending, scheduled, and queued Tuco AI messages for a lead — use when a lead replies Stop or opts out to instantly halt outbound campaigns.
POST
/
api
/
messages
/
cancel-for-lead
Cancel Pending Messages
curl --request POST \
--url https://app.tuco.ai/api/messages/cancel-for-lead \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"leadId": "<string>",
"recipientPhone": "<string>",
"recipientEmail": "<string>"
}
'import requests
url = "https://app.tuco.ai/api/messages/cancel-for-lead"
payload = {
"leadId": "<string>",
"recipientPhone": "<string>",
"recipientEmail": "<string>"
}
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({leadId: '<string>', recipientPhone: '<string>', recipientEmail: '<string>'})
};
fetch('https://app.tuco.ai/api/messages/cancel-for-lead', 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://app.tuco.ai/api/messages/cancel-for-lead",
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([
'leadId' => '<string>',
'recipientPhone' => '<string>',
'recipientEmail' => '<string>'
]),
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://app.tuco.ai/api/messages/cancel-for-lead"
payload := strings.NewReader("{\n \"leadId\": \"<string>\",\n \"recipientPhone\": \"<string>\",\n \"recipientEmail\": \"<string>\"\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://app.tuco.ai/api/messages/cancel-for-lead")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"leadId\": \"<string>\",\n \"recipientPhone\": \"<string>\",\n \"recipientEmail\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/messages/cancel-for-lead")
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 \"leadId\": \"<string>\",\n \"recipientPhone\": \"<string>\",\n \"recipientEmail\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"cancelledCount": 123,
"jobsRemovedFromQueue": 123,
"leadId": "<string>",
"messageIds": [
"<string>"
]
}Call this endpoint when a lead replies “Stop”, “Unsubscribe”, or any opt-out keyword. It cancels all pending, scheduled, and queued messages for that lead and removes their jobs from the processing queue.
Authentication
Authorization: Bearer tuco_sk_xxxxxxxxxxxxx
Request body
You must provide at least one of these identifiers:string
Cancel all pending messages for this lead ID. Most direct — skips the lookup step.
string
Phone number (E.164 format). Tuco looks up the lead by phone (including altPhone1-3), then cancels all pending messages for that lead. If no lead is found, cancels messages matching this phone directly.
string
Email address. Tuco looks up the lead by email (including altEmail1-3), then cancels all pending messages for that lead. If no lead is found, cancels messages matching this email directly.
How it works
1
Identify the lead
If
leadId is provided, use it directly. Otherwise, look up the lead by recipientPhone or recipientEmail (checks primary + alt fields).2
Find cancellable messages
Query all messages for this lead (or address) with status
queued, pending, or scheduled.3
Cancel in database
Bulk update all matching messages to
status: "cancelled".4
Remove from queue
Remove each message’s BullMQ processing job so it won’t be picked up by the worker.
Examples
curl -X POST "https://app.tuco.ai/api/messages/cancel-for-lead" \
-H "Authorization: Bearer tuco_sk_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "recipientPhone": "+14425550100" }'
curl -X POST "https://app.tuco.ai/api/messages/cancel-for-lead" \
-H "Authorization: Bearer tuco_sk_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "recipientEmail": "john@example.com" }'
curl -X POST "https://app.tuco.ai/api/messages/cancel-for-lead" \
-H "Authorization: Bearer tuco_sk_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "leadId": "69b2c41d352a78479d2c623b" }'
Success response (200)
{
"success": true,
"cancelledCount": 3,
"jobsRemovedFromQueue": 2,
"leadId": "69b2c41d352a78479d2c623b",
"messageIds": [
"69dba0001111111111111111",
"69dba0001111111111111112",
"69dba0001111111111111113"
]
}
boolean
Always
true on success (even when 0 messages cancelled).integer
Number of messages set to
cancelled in the database.integer
Number of BullMQ jobs removed. May be less than
cancelledCount if some jobs already started processing.string
The resolved lead ID (may be
null if no lead was found and cancellation was done by address).string[]
IDs of all cancelled messages.
Error responses
| Status | When | Body |
|---|---|---|
400 | No identifier provided | { "error": "Either leadId, recipientPhone, or recipientEmail is required" } |
400 | Invalid leadId format | { "error": "Invalid leadId" } |
401 | Missing or invalid API key | { "error": "Unauthorized" } |
500 | Server error | { "error": "Internal server error" } |
Usage with n8n / automation
Wire this into your reply handler workflow. When a lead replies with an opt-out keyword, call this endpoint with their phone number to stop all future sequence messages:{
"method": "POST",
"url": "https://app.tuco.ai/api/messages/cancel-for-lead",
"headers": { "Authorization": "Bearer tuco_sk_xxxxxxxxxxxxx" },
"body": { "recipientPhone": "{{ $json.phone }}" }
}