Cancel a scheduled message

Calls off a message you scheduled with scheduled_at, before it is released for sending. The message moves to CANCELLED, which is terminal, and it is never sent.

Only a SCHEDULED message can be cancelled. A message that has already been released — queued, in flight, or finished — returns 409. The release sweeper runs continuously, so a cancellation sent within about a minute of the scheduled instant can lose that race and be refused; cancel with time to spare.

One message per call. A send to many recipients, or on several channels, becomes one message per recipient × channel, each with its own id; cancel the ones you want stopped. There is no bulk cancellation.

Cancelling is free, and nothing was charged for the hold in the first place — a scheduled message is only priced and billed at release.

A cancellation fires message.cancelled on your webhook, exactly once, carrying the scheduled_at that was called off so a consumer can un-record the send it was expecting. Cancelling the same message again returns this same response and fires no second event. Idempotency-Key is honoured.

A cancelled message cannot be resent — send a new one.

The response is the message itself, in the same shape GET /v3/messages/{id} returns, so the status you read back is the one now stored on the row, and scheduled_at is the time it would have gone out. GET /v3/messages/{id} keeps reporting both afterwards.

events is the message lifecycle as it stood immediately before the cancellation: the CANCELLED entry is written asynchronously and lands a moment later, so read status for the outcome. If you need the entry itself, re-fetch the message after a few seconds — GET /v3/messages/{id} is cached briefly, so an immediate re-fetch can still show the pre-cancellation state.

POST
/v3/messages/{id}/cancel

Path Parameters

id*string

Message ID

Formatuuid

Header Parameters

Idempotency-Key?string

Unique key to ensure idempotent request processing. Must be 1-255 alphanumeric characters, hyphens, or underscores. Responses are cached for 24 hours per key per customer.

Match^[a-zA-Z0-9_-]+$
Lengthlength <= 255
x-profile-id?string

Profile UUID to scope the request to a child profile. Only organization API keys can use this header. The profile must belong to the calling organization.

Formatuuid

Request Body

application/json
  1. body

Base request for mutation operations (POST, PUT, PATCH) with sandbox mode support

sandbox?boolean

Sandbox flag - when true, the operation is simulated without side effects Useful for testing integrations without actual execution

Response Body

The message is cancelled and will not be sent

application/json
  1. response

Standard API response envelope for all v3 endpoints

success?boolean

Indicates whether the request was successful

data?|null

The response data (null if error)

error?|null

Error details (null if successful)

meta?

Metadata about the request and response

Example Request
curl -X POST 'https://api.sent.dm/v3/messages/8ba7b830-9dad-11d1-80b4-00c04fd430c8/cancel' \  -H 'Idempotency-Key: req_abc123_retry1' \  -H 'Content-Type: application/json' \  -d '{  "sandbox": false}'
Example Response
{  "success": true,  "data": {    "scheduled_at": "2026-10-01T07:00:00+00:00",    "id": "8ba7b830-9dad-11d1-80b4-00c04fd430c8",    "customer_id": "00000000-0000-0000-0000-000000000000",    "contact_id": "00000000-0000-0000-0000-000000000000",    "phone": "+14155551234",    "phone_international": "+1 415-555-1234",    "region_code": "US",    "template_id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",    "template_name": "order_confirmation",    "template_category": null,    "channel": "sms",    "message_body": null,    "status": "CANCELLED",    "direction": "OUTBOUND",    "created_at": "2026-09-30T11:59:00+00:00",    "price": null,    "active_contact_price": null,    "events": null,    "reason_code": null,    "reason": null  },  "error": null,  "meta": {    "request_id": "req_7X9zKp2jDw",    "timestamp": "2026-11-12T15:32:26.7629803+00:00",    "version": "v3"  }}