Team Chat Bots
The Team Chat Bot API lets your own systems post into your workspace's Team Chat -- a deploy pipeline announcing a release in #engineering, a monitoring job dropping an alert into #ops, an order system messaging a specific teammate -- using the credentials you already have.
Authentication
Bots reuse your existing API key or OAuth authentication -- there is no separate bot token or bot credential. Add the teamchat:write scope to the token or OAuth client you want to post with:
Authorization: Bearer s7_api_your_token_here
Content-Type: application/json
Required scope: teamchat:write
teamchat:write is written without an underscore, unlike the human-facing Team Chat scopes (team_chat:read, team_chat:create, team_chat:settings, team_chat:admin). It's a separate scope that grants only the two bot-sending endpoints on this page -- it does not grant read access to Team Chat or any of the human-facing endpoints.
How a bot is identified
A bot message is never attributed to a real teammate:
- It always renders with a bot avatar in the UI -- never a person's profile picture.
- You can set any display name per message via
bot_name, up to 50 characters. There's no name registration or allowlist -- send"bot_name": "Deploy Bot"on one call and"bot_name": "Release Bot"on the next if you like. - The response and any resulting webhook always carry
sender_type: "bot"/is_bot: true, so consumers can reliably tell a bot message apart from a teammate's.
Request body
Both endpoints below accept the same body:
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Message text, 1-40,000 characters. |
bot_name | string | No | Display name for the bot, up to 50 characters. Sending a longer name returns 422 Unprocessable Entity. If omitted, a default bot name is used. |
attachment_ids | array of string | No | IDs of attachments already uploaded to your workspace (see Upload an Attachment). |
attachments | array of object | No | Richer attachment inputs -- see below. |
Attachments
Bots can send image, video, audio, and document attachments the same two ways any other SendSeven message can: by referencing an already-uploaded attachment ID, or by handing SendSeven a public URL to fetch. Both forms can be mixed in the same request across multiple attachments, and attachment_ids and attachments are merged together in order.
Each item in attachments is either {"id": "..."} or {"url": "...", "filename": "..."} -- exactly one of id/url per item:
{
"attachments": [
{ "id": "9b3f1a8e-7c2d-4e5b-9f01-12a3b4c5d6e7" },
{ "url": "https://cdn.example.com/report.png", "filename": "report.png" }
]
}
A url item is downloaded into SendSeven's own storage using the exact same ingestion and validation as Create an Attachment from a URL -- the same SSRF protections, the same MIME allowlist, and the same 50 MB size limit. An oversized or unsupported file is rejected with 422 Unprocessable Entity.
See Attachments Overview for the full attachment lifecycle if you haven't used the attachments API before.
Post to a Channel as a Bot
POST /api/v1/team-chat/bot/channels/{channel_ref}/messages
channel_ref accepts either the channel's UUID or its name, with or without a leading # (3f7c1a92-..., #general, and general are all valid).
Required scope: teamchat:write
Rate limit: 60 requests per minute per tenant (shared with the DM endpoint below -- see Rate Limits).
Requirements
- The target channel must have "Allow Bots to send messages" enabled, or the request is rejected with
403. The auto-created#generaland#shoutboxchannels have it on by default; channels you create are off by default unless you passallow_bots: truewhen creating them (or turn it on in that channel's settings afterwards). - Bots can post to any channel with bot posting enabled -- including the auto-created
#generaland#shoutboxchannels -- except the Knowledge Base channel, which always returns403, regardless ofallow_bots. - The channel must not be archived -- posting to an archived channel returns
409.
Cross-tenant sending
Add ?source_tenant_id= to post into a channel that belongs to a different tenant than the one your token authenticates as -- for example, an integration acting on behalf of an external Team Chat channel shared with another workspace.
curl
curl -X POST "https://api.sendseven.com/api/v1/team-chat/bot/channels/general/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"text": "Deploy finished successfully :rocket:",
"bot_name": "Deploy Bot"
}'
Node.js
const response = await fetch(
"https://api.sendseven.com/api/v1/team-chat/bot/channels/general/messages",
{
method: "POST",
headers: {
Authorization: "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
"Content-Type": "application/json",
},
body: JSON.stringify({
text: "Deploy finished successfully :rocket:",
bot_name: "Deploy Bot",
}),
}
);
const message = await response.json();
console.log(message.id);
Response (201 Created)
{
"id": "9b21e7c4-13ad-4f0a-8f52-6c9d0e3a7711",
"channel_id": "3f7c1a92-5d84-4b1e-9a3c-70e2f8d41b05",
"dm_conversation_id": null,
"sender_type": "bot",
"is_bot": true,
"bot_name": "Deploy Bot",
"sender_user_id": null,
"content": "Deploy finished successfully :rocket:",
"message_type": "text",
"attachment_ids": null,
"attachments": null,
"mentioned_user_ids": [],
"mentioned_all": false,
"created_at": "2026-08-04T14:30:00Z"
}
Send a Direct Message as a Bot
POST /api/v1/team-chat/bot/users/{user_id}/messages
user_id is the recipient -- a user in your own workspace.
Required scope: teamchat:write
Rate limit: 60 requests per minute per tenant (shared with the channel endpoint above -- see Rate Limits).
Unlike channel posts, a bot DM is always allowed once you're authenticated -- there is no per-channel or per-user permission gate for direct messages, and no equivalent of allow_bots to turn on. The recipient must be an active member of your workspace; targeting a user in a different tenant returns 404 Not Found.
curl
curl -X POST "https://api.sendseven.com/api/v1/team-chat/bot/users/4c8a0f11-92be-4d7a-b0f3-1d5e6a82c904/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"text": "Your export is ready to download.",
"bot_name": "Export Bot"
}'
Node.js
const userId = "4c8a0f11-92be-4d7a-b0f3-1d5e6a82c904";
const response = await fetch(
`https://api.sendseven.com/api/v1/team-chat/bot/users/${userId}/messages`,
{
method: "POST",
headers: {
Authorization: "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
"Content-Type": "application/json",
},
body: JSON.stringify({
text: "Your export is ready to download.",
bot_name: "Export Bot",
}),
}
);
const message = await response.json();
console.log(message.dm_conversation_id);
Response (201 Created)
The same shape as the channel response, with dm_conversation_id populated and channel_id null. sender_user_id is set to the token's own user (the bot identity itself still travels in bot_name):
{
"id": "a7d3f912-88bc-4e01-9a2f-51c6d0e8f223",
"channel_id": null,
"dm_conversation_id": "77c2b0e5-4a19-4c8f-9d63-2b8e1f704ac3",
"sender_type": "bot",
"is_bot": true,
"bot_name": "Export Bot",
"sender_user_id": "9e1f2a3b-4c5d-6e7f-8091-a2b3c4d5e6f7",
"content": "Your export is ready to download.",
"message_type": "text",
"attachment_ids": null,
"attachments": null,
"mentioned_user_ids": [],
"mentioned_all": false,
"created_at": "2026-08-04T14:32:00Z"
}
Sending a bot DM does not emit team_chat.message.created -- only channel messages do. If you're building something that needs to react to what happens after a bot DM, route the conversation through a channel instead. See Team Chat Webhook for the full explanation.
Response fields
| Field | Type | Description |
|---|---|---|
id | string | Message ID. |
channel_id | string | null | Set for channel sends, null for DMs. |
dm_conversation_id | string | null | Set for DM sends, null for channel sends. |
sender_type | string | Always "bot". |
is_bot | boolean | Always true. |
bot_name | string | null | The display name used for this message. |
sender_user_id | string | null | null for channel bot messages; the token's own user ID for DM bot messages. |
content | string | The message text you sent. |
message_type | string | "text", or "image"/"video"/"audio"/"document" when the message carries a single non-text attachment. |
attachment_ids | array | null | Linked attachment IDs. |
attachments | array | null | Enriched attachment info (id, filename, content_type, file_size, public_url, and width/height/duration where applicable). |
mentioned_user_ids | array | User IDs @mentioned in the text. |
mentioned_all | boolean | Whether @all was used. |
created_at | string | ISO 8601 creation timestamp. |
Rate limits
Both endpoints share a single 60 requests per minute budget per tenant -- sending 40 channel messages and 30 DMs in the same minute from the same tenant will hit the limit at message 61 regardless of which endpoint they came through. Exceeding it returns:
{ "detail": "Rate limit exceeded. Please slow down." }
with HTTP status 429.
Errors
| Status | Cause |
|---|---|
403 Bots cannot post to the knowledge base channel | channel_ref resolved to the Knowledge Base channel, which never accepts bot posts. |
403 This channel does not allow bot messages | The target channel has "Allow Bots to send messages" turned off. |
404 Channel not found | channel_ref didn't resolve to any channel in the tenant. |
404 User not found | (DM endpoint) The target user_id isn't an active member of the tenant -- also returned for a cross-tenant target. |
409 Channel is archived | The target channel is archived. |
422 | text was empty or over 40,000 characters, bot_name was over 50 characters, or an attachments item failed validation (missing/invalid id/url, unsupported MIME type, or over the 50 MB size limit). |
429 Rate limit exceeded | More than 60 requests/min from this tenant across both endpoints. |
Next
- Team Chat Webhook -- react to messages posted into bot-enabled channels (and the DM caveat above).
- Integration Recipes -- Zapier, n8n, and Make.com flows built on this API.
- Team Chat Overview -- where "Allow Bots to send messages" lives in the app.