Skip to main content

Send Text Messages

SendSeven lets you send text messages to existing conversations or directly to contacts on any supported channel. This guide covers both approaches with full examples.

Required Scopes​

ScopePurpose
messages:createSend messages to conversations
messages:createSend messages to contacts

Send a Message to a Conversation​

If you already have a conversation ID (from a webhook event or the Conversations API), you can send a message directly into it.

POST /api/v1/messages

Request Body​

FieldTypeRequiredDescription
conversation_idstringYesTarget conversation ID
textstringYesMessage text content
message_typestringYesMust be text

curl​

curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_8f3a2b1c",
"text": "Your order #1234 has been shipped! Tracking number: TR-98765.",
"message_type": "text"
}'

Python​

import requests

BASE_URL = "https://api.sendseven.com/api/v1"
HEADERS = {
"Authorization": "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
"Content-Type": "application/json",
}

response = requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"conversation_id": "conv_8f3a2b1c",
"text": "Your order #1234 has been shipped! Tracking number: TR-98765.",
"message_type": "text",
},
)
message = response.json()
print(f"Message sent: {message['id']} - Status: {message['status']}")

JavaScript​

const BASE_URL = "https://api.sendseven.com/api/v1";
const HEADERS = {
"Authorization": "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
"Content-Type": "application/json",
};

const response = await fetch(`${BASE_URL}/messages`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
conversation_id: "conv_8f3a2b1c",
text: "Your order #1234 has been shipped! Tracking number: TR-98765.",
message_type: "text",
}),
});
const message = await response.json();
console.log(`Message sent: ${message.id}`);

Response​

{
"id": "msg_c3d4e5f6",
"conversation_id": "conv_8f3a2b1c",
"direction": "outbound",
"text": "Your order #1234 has been shipped! Tracking number: TR-98765.",
"message_type": "text",
"status": "pending",
"created_at": "2026-02-10T15:10:00Z"
}

Send a Message Without a Conversation ID​

You can also send messages without an existing conversation ID. SendSeven automatically creates or resumes the appropriate conversation. The POST /api/v1/messages endpoint supports three resolution modes:

POST /api/v1/messages

Mode 1: Send via Contact Method ID (Cleanest)​

If you know the contact method ID (from the Contacts API), this is the simplest way to send. The system resolves the recipient and channel automatically.

FieldTypeRequiredDescription
contact_method_idstringYesContact method ID (resolves recipient and channel)
textstringYesMessage text content
message_typestringNoDefault: text
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"contact_method_id": "cm_a1b2c3d4",
"text": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"message_type": "text"
}'

Mode 2: Send to Contact with Channel (Explicit)​

Specify the contact, channel, and optionally a recipient address. The to field is optional -- when omitted, SendSeven resolves the recipient from the contact's stored details.

FieldTypeRequiredDescription
contact_idstringYesTarget contact ID
channel_idstringNoChannel to send through. When omitted, SendSeven auto-selects an active channel for the contact (priority: WhatsApp, Telegram, Messenger, Instagram, SMS, Email). Pass it to disambiguate when the contact is reachable on several channels.
textstringYesMessage text content
message_typestringNoDefault: text
tostringNoRecipient address (e.g. phone number, Telegram ID, email). Resolved automatically if omitted.
WhatsApp Business-scoped user IDs

to also accepts a WhatsApp Business-scoped user ID such as US.13491208655302741918 -- pass it verbatim, periods included, together with the channel_id of the WhatsApp channel it belongs to. SendSeven recognizes the format, addresses the message correctly, and stores the value as a whatsapp_bsuid contact method on the contact. See Migrating to BSUIDs.

curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "contact_d4e5f6a7",
"channel_id": "ch_tg_001",
"text": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"message_type": "text"
}'

Python​

# Mode 1: Via contact method ID
response = requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"contact_method_id": "cm_a1b2c3d4",
"text": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"message_type": "text",
},
)
message = response.json()
print(f"Sent to contact: {message['id']}")

# Mode 2: Via contact + channel
response = requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"contact_id": "contact_d4e5f6a7",
"channel_id": "ch_tg_001",
"text": "Your appointment reminder: tomorrow at 2 PM.",
"message_type": "text",
},
)
message = response.json()
print(f"Sent to contact: {message['id']}")

JavaScript​

// Mode 1: Via contact method ID
const response = await fetch(`${BASE_URL}/messages`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
contact_method_id: "cm_a1b2c3d4",
text: "Your appointment reminder: tomorrow at 2 PM.",
message_type: "text",
}),
});
const message = await response.json();
console.log("Sent:", message.id);

Response​

{
"id": "msg_d4e5f6g7",
"conversation_id": "conv_new_or_existing",
"direction": "outbound",
"text": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"message_type": "text",
"status": "pending",
"created_at": "2026-02-10T15:20:00Z"
}
tip

When sending without a conversation ID, SendSeven automatically finds or creates the right conversation. Use contact_method_id when possible -- it is the cleanest approach and requires the fewest fields.

Message Status Values​

After sending, a message progresses through these statuses:

StatusDescription
pendingMessage accepted and queued for delivery (the initial status returned by POST /messages)
sentMessage sent to the channel provider
deliveredDelivery confirmed by the channel
readMessage read by the recipient (if supported)
failedDelivery failed
warning

For WhatsApp, you can only send free-form text messages within a 24-hour window after the contact's last message. Outside this window, you must use an approved template.

Instagram and Messenger: human agent replies​

Instagram and Facebook Messenger allow a free-form reply within 24 hours of the customer's last message. With Meta's HUMAN_AGENT message tag, a reply written personally by a human agent can still be sent up to 7 days after that message.

Opt in per message with channel_options:

{
"conversation_id": "b5d8e2a1-4c3f-4a8b-9d2e-1f6a7b8c9d0e",
"text": "Sorry for the late reply, your replacement ships today.",
"channel_options": { "tag": "HUMAN_AGENT" }
}
channel_options.tagBehaviour
"HUMAN_AGENT"24 hours to 7 days after the customer's last message: sent with the HUMAN_AGENT tag. Within 24 hours or after 7 days: sent untagged.
"none"Never tagged. Accepted on every channel as a no-op.
omittedAPI keys and OAuth tokens: never tagged. Agents signed in to the SendSeven app are tagged automatically.
warning

Use HUMAN_AGENT only for replies a human agent personally sends. A reply drafted with AI help counts when the agent reviews it and sends it. Never use it for automated, bot or AI messages that go out without a person sending them. Meta may revoke your messaging access for misuse.

SendSeven does not block a send because of the messaging window. Meta decides: if Meta rejects a message sent with POST /messages, it gets status failed with an explanatory error_message, and the message.failed webhook fires. A tagged message carries meta.message_tag: "HUMAN_AGENT". Setting channel_options.tag to "HUMAN_AGENT" on any other channel returns 422 channel_option_not_supported. To show the window in your own UI, read reply_window on the conversation.

Error Responses​

Errors with a machine-readable code return it inside detail, under the key shown. Other errors return detail as a message string. See Error Handling for the response shapes.

StatusdetailDescription
400"Either 'conversation_id', 'contact_method_id', or 'contact_id' must be provided."No recipient given
400{"error": "EMPTY_MESSAGE", "message": ...}message_type: "text" without text and without attachments
400{"error": "WHATSAPP_WINDOW_EXPIRED", "message": ...}WhatsApp: the 24-hour customer service window has closed, also on Click-to-WhatsApp ad conversations. Send a template
400{"error": "WHATSAPP_NO_CUSTOMER_MESSAGE", "message": ...}WhatsApp: the contact has never written to you. Send a template
400{"error": "EMAIL_SUBJECT_REQUIRED", "message": ...}Email: no subject and the conversation has none to reuse
401"Invalid or expired API token"Token is invalid or expired
403"Missing required permission(s): messages:create"Token lacks the scope
403{"error": "SENDER_NOT_ALLOWED", "message": ..., "sender_id": ...}Email: you may not send from the chosen sender_id
404"Conversation not found", "Contact not found", "Contact method not found", "Channel not found"Unknown ID, or it belongs to another workspace
409{"error": "CHANNEL_DISCONNECTED", "message": ..., "channel_id": ..., "channel_type": ..., "disconnection_reason": ...}The channel is disconnected. Reconnect it in Settings > Channels
409{"error": "CONTACT_BLOCKED", "message": ..., "contact_id": ...}The contact is blocked
422{"error_code": "channel_option_not_supported", "message": ..., "channel_type": ..., "option": "tag"}channel_options.tag: "HUMAN_AGENT" on a channel other than Instagram / Messenger
422list of field errorsInvalid request body

RCS sends can also return 402 or 409 with {"code": ..., "message": ...}, see RCS wallet errors.

If the send fails after the request was accepted (the channel or provider rejects it, or SendSeven cannot deliver it, for example because the SMS balance is empty), the request still returns 201; the message moves to failed and the message.failed webhook fires once, after the last attempt. The rich send endpoints (interactive, contacts, sticker, carousel) send right away and report a failed send in their response; they fire message.failed once as well. They also apply the same WhatsApp 24-hour window check as POST /messages to free-form sends (interactive, contacts, sticker), with the same WHATSAPP_WINDOW_EXPIRED / WHATSAPP_NO_CUSTOMER_MESSAGE errors.

Next Steps​