curl --request POST \
--url https://api.reply.io/v3/direct-sends/{reference}/cancel \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.reply.io/v3/direct-sends/{reference}/cancel"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.reply.io/v3/direct-sends/{reference}/cancel', 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.reply.io/v3/direct-sends/{reference}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
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.reply.io/v3/direct-sends/{reference}/cancel"
req, _ := http.NewRequest("POST", 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.post("https://api.reply.io/v3/direct-sends/{reference}/cancel")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/direct-sends/{reference}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"reference": "email-48213",
"channel": "email",
"contactId": 42,
"state": "cancelled",
"sendAfter": "2026-11-20T09:00:00Z",
"estimatedSendAt": null,
"sentAt": null,
"messageId": null,
"failureCode": null,
"failureReason": null,
"missingVariables": null,
"renderedSubject": null,
"renderedBody": null,
"cancelledAt": "2026-11-19T16:12:03Z"
}{
"title": "Unauthorized",
"status": 401,
"detail": "Authentication credentials are missing or invalid."
}{
"title": "Forbidden",
"status": 403,
"detail": "The send belongs to a teammate you cannot act for.",
"code": "directSend.notOwned"
}{
"title": "Not Found",
"status": 404,
"detail": "The send was not found.",
"code": "directSend.notFound"
}{
"title": "Conflict",
"status": 409,
"detail": "The send has already gone out or is going out right now.",
"code": "directSend.alreadySent"
}{
"title": "Too Many Requests",
"status": 429,
"detail": "API calls quota exceeded! maximum admitted 100 per 1m."
}{
"title": "Service Unavailable",
"status": 503,
"detail": "The cancel could not be confirmed and the send is still queued; retry it.",
"code": "directSend.cancelNotConfirmed"
}Cancel a direct send
Coming soon. This endpoint will be available by mid November 2026.
contacts:operate scope (or a broader one that includes it).
Cancel a direct send that is still queued or waitingForSchedule, by its reference, or a LinkedIn send for later (linkedin-scheduled-<id>) that is sending but still waits in the account’s queue. Answers the send in the cancelled state. Cancelling a send already cancelled answers it again, so the call is safe to repeat.
A send that is sending or sent answers 409 directSend.alreadySent, and one that has failed answers 409 directSend.alreadyFailed. A LinkedIn send for later (linkedin-scheduled-<id>) that is being handed to the account’s queue, or that the account has started, answers 409 directSend.alreadySending. A send can start the moment it is due, so a cancel made very close to sendAfter may find it already gone.
An unconfirmed cancel of a send not yet gone answers 503 directSend.cancelNotConfirmed; retry it. For a LinkedIn send for later that also covers one being picked up at that moment while it still reads queued or waitingForSchedule, and one waiting in the account’s queue whose cancel met a concurrent change.
An email send made by a teammate can be cancelled when you may act for that teammate, otherwise it answers 403 directSend.notOwned. A LinkedIn send can be cancelled by every teammate who can see its LinkedIn account.
curl --request POST \
--url https://api.reply.io/v3/direct-sends/{reference}/cancel \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.reply.io/v3/direct-sends/{reference}/cancel"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.reply.io/v3/direct-sends/{reference}/cancel', 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.reply.io/v3/direct-sends/{reference}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
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.reply.io/v3/direct-sends/{reference}/cancel"
req, _ := http.NewRequest("POST", 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.post("https://api.reply.io/v3/direct-sends/{reference}/cancel")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/direct-sends/{reference}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"reference": "email-48213",
"channel": "email",
"contactId": 42,
"state": "cancelled",
"sendAfter": "2026-11-20T09:00:00Z",
"estimatedSendAt": null,
"sentAt": null,
"messageId": null,
"failureCode": null,
"failureReason": null,
"missingVariables": null,
"renderedSubject": null,
"renderedBody": null,
"cancelledAt": "2026-11-19T16:12:03Z"
}{
"title": "Unauthorized",
"status": 401,
"detail": "Authentication credentials are missing or invalid."
}{
"title": "Forbidden",
"status": 403,
"detail": "The send belongs to a teammate you cannot act for.",
"code": "directSend.notOwned"
}{
"title": "Not Found",
"status": 404,
"detail": "The send was not found.",
"code": "directSend.notFound"
}{
"title": "Conflict",
"status": 409,
"detail": "The send has already gone out or is going out right now.",
"code": "directSend.alreadySent"
}{
"title": "Too Many Requests",
"status": 429,
"detail": "API calls quota exceeded! maximum admitted 100 per 1m."
}{
"title": "Service Unavailable",
"status": 503,
"detail": "The cancel could not be confirmed and the send is still queued; retry it.",
"code": "directSend.cancelNotConfirmed"
}Authorizations
Authenticate every request with a Bearer token. Pass your Reply API key in the
Authorization header:
Authorization: Bearer <your-api-key>
Get your API key from the Reply dashboard: Settings → API Key.
Path Parameters
The send's reference, email-<id>, linkedin-<id> or linkedin-scheduled-<id>. A reference not of this form answers 404 directSend.notFound.
Response
The send, cancelled.
One direct send - an email or a LinkedIn action sent to a contact outside any sequence - read by its reference.
The reference the send route answered with, email-<id>, linkedin-<id> or linkedin-scheduled-<id>. Treat it as opaque.
"email-48213"
The channel a direct send goes out on.
email, linkedIn ID of the contact the send is addressed to.
Where a direct send stands.
| Value | Meaning |
|---|---|
queued | Accepted and waiting for its moment; it can still be cancelled. |
waitingForSchedule | Accepted and waiting for its schedule to open, more than ten minutes after acceptance; estimatedSendAt says when. A schedule open sooner queues the send at once (queued). It can still be cancelled. |
sending | Being sent right now - for a LinkedIn send, handed to the account's queue and waiting or running there. It can no longer be cancelled, except a LinkedIn send for later that waits in the account's queue and has not been started. |
sent | Gone out. |
failed | Did not go out, or its outcome is unknown; failureCode and failureReason say why. |
cancelled | Cancelled while queued; it will not go out. |
queued, waitingForSchedule, sending, sent, failed, cancelled UTC moment after which the send is attempted - the requested sendAfter, when the send was accepted, or the schedule opening it was queued for. Null while the send waits for its schedule; estimatedSendAt is set then. A send cancelled while waiting, or failed because its schedule or the contact's time zone was gone, reads the requested sendAfter when it was given one, and null when it was made with a schedule and no sendAfter - it was never given a moment. This holds for email and LinkedIn alike.
When the schedule the send waits for next opens, as read now; the send goes out within about 10 minutes of it. A schedule open at acceptance or opening within the next ten minutes queued the send for that moment at once, so it reads queued with sendAfter and this null. Set in the waitingForSchedule state only, and null there when the schedule does not open within 30 days of the moment the send waits from (the requested sendAfter, or now once that has passed).
UTC moment the send went out. Set in the sent state only.
Message-ID of the email once it has gone out. Always null for LinkedIn.
What the failure was, as the code the send route answers that failure with. Set in the failed state when the failure has a code of its own; null otherwise, and then failureReason tells it.
| Value | Meaning |
|---|---|
contact.missingVariables | The email's template uses variables that have no value for the contact; missingVariables names them. Nothing went out. |
contact.renderingFailed | The email's template could not be rendered for the contact. Nothing went out. |
contact.notFound | The contact has no valid email address. Nothing went out. |
contact.contactInBlackList | The contact is on the blacklist. Nothing went out. |
contact.scheduleNotFound | The schedule the send waited for was deleted. Nothing went out. |
contact.scheduleHasNoSlot | The schedule the send waited for was left with no active day that has a time range, so it never opens. Nothing went out. |
contact.contactTimeZoneMissing | The send waited for its schedule in the contact's time zone (useContactTimeZone true), and the contact no longer has one. Nothing went out. |
directSend.dispatchWindowMissed | The send was never attempted: its moment passed without it being picked up. Nothing went out; sending again is safe. |
directSend.dispatchInterrupted | The send was attempted but its outcome is unknown. It may have gone out; check before sending again. |
directLinkedIn.weeklyLimitReached | LinkedIn is limiting the account's connection requests, or refused this one for the account's limit; for a send for later, the limit outlasted its retry window. Nothing went out; send again once the account is no longer limited. |
directLinkedIn.dailyLimitReached | A LinkedIn send for later stayed blocked by the account's daily allowance for the action until its retry window ended. Nothing went out. |
directLinkedIn.missedWindow | A LinkedIn send for later was due while nothing could hand it to the account's queue, and its retry window ended first. Nothing went out; sending again is safe. |
directLinkedIn.scheduleNotFound | The schedule a LinkedIn send waited for was deleted. Nothing went out. An email send reads contact.scheduleNotFound for the same failure, so a client handling both channels should expect both codes. |
directLinkedIn.scheduleHasNoSlot | The schedule a LinkedIn send waited for was left with no active day that has a time range, so it never opens. Nothing went out. An email send reads contact.scheduleHasNoSlot for the same failure. |
directLinkedIn.contactTimeZoneMissing | A LinkedIn send waited for its schedule in the contact's time zone (useContactTimeZone true), and the contact no longer has one. Nothing went out. |
directLinkedIn.alreadyConnected | A LinkedIn connection request for later found the contact already connected when it became due. Nothing went out. |
directLinkedIn.notConnected | A LinkedIn connection request for later found an invitation to the contact still pending when it became due, or a LinkedIn message for later found the contact not connected. Nothing went out. |
directLinkedIn.contactNotFound | The contact of a LinkedIn send for later was deleted, or could no longer be found, before the send became due. Nothing went out. |
A LinkedIn send for later that is due is checked again before it goes to the account's queue; a refusal found then, for example a LinkedIn account no longer connected, fails it with the directLinkedIn.* code the send route answers that refusal with. Nothing went out. A failure with no code of its own, such as an unexpected error, has failureCode null and says what happened in failureReason.
"directSend.dispatchWindowMissed"
Why the send failed, as a sentence for people to read. Set in the failed state only. Use failureCode to act on the failure.
"The send was never attempted: its dispatch window passed before anything picked it up."
The variables the email's template uses that have no value for the contact, by name without braces. Set with failureCode contact.missingVariables only.
The subject as sent, its template rendered for the contact. Email, sent state only; null when the sent message can no longer be read.
The body as sent, with the signature and the tracking - its template rendered for the contact, the sending account's signature added, and the links and open pixel of the tracking when it is on. HTML, or text for an email with no HTML part. Email, sent state only; null when the sent message can no longer be read.
UTC moment the send was cancelled. Set in the cancelled state only.