Skip to content

Incoming Webhooks

Incoming webhooks let an external service push a message into a specific DonutChat chat with a single HTTPS request. They’re one-way, require no bot identity, and can be enabled or revoked per chat.

DirectionExternal service → chat
AuthOpaque token carried in the URL path
TransportPOST over HTTPS (application/json)
Typical use caseAlerts, CI/CD notifications, monitoring, cron-triggered updates

Pick an incoming webhook when you want the simplest possible “post a message into this chat” path and the sender doesn’t need a reusable identity. If you need to reply as a named bot across multiple chats, use Bot Send instead. If you also need to receive chat events, you need a bot — see Bot Receive.

import { Steps } from ‘@astrojs/starlight/components’;

  1. Open the chat where you want the webhook to post.
  2. Open chat settings → WebhooksCreate webhook.
  3. Fill in a name and (optionally) a description, default display name, and default avatar.
  4. Tap Create. DonutChat shows the full webhook URL — this is the only time the token is visible.
  5. Copy the URL and paste it into your external system’s webhook configuration.

import { Aside } from ‘@astrojs/starlight/components’;

POST https://api.donutchat.com/webhooks/v1/{webhook_id}/{token}

The token authenticates the request via the URL path. Do not set an Authorization header — the token lives in the URL.

HeaderValue
Content-Typeapplication/json
{
"content": "Build #482 passed on main.",
"username": "CI Bot",
"avatar_url": "https://example.com/ci-avatar.png"
}
FieldTypeRequiredNotes
contentstringyesMessage body. Max 4000 characters.
usernamestringnoOverrides the webhook’s default display name for this message.
avatar_urlstringnoOverrides the webhook’s default avatar URL for this message.

import { Tabs, TabItem } from ‘@astrojs/starlight/components’;

Terminal window
curl -X POST \
"https://api.donutchat.com/webhooks/v1/1234/YOUR_WEBHOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Build #482 passed on main.",
"username": "CI Bot"
}'
const url = 'https://api.donutchat.com/webhooks/v1/1234/YOUR_WEBHOOK_TOKEN';
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: 'Build #482 passed on main.',
username: 'CI Bot',
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(`${err.error}: ${err.message}`);
}
const { message_id, chat_id } = await response.json();
console.log('Posted message', message_id, 'to chat', chat_id);

Success — HTTP 200:

{
"ok": true,
"message_id": 98765,
"chat_id": 1234
}

Error — HTTP 4xx or 5xx:

{
"ok": false,
"error": "<code>",
"message": "<human-readable>"
}
HTTP statuserror codeMeaningWhat to do
400invalid_jsonBody was not valid JSONSend a well-formed JSON body
400missing_contentcontent field is empty or missingProvide a non-empty content string
400content_too_longcontent exceeds 4000 charactersSplit or shorten the message
401unauthorizedToken is invalid, or the webhook id in the URL doesn’t existConfirm the full URL including token; rotate the token if compromised
403webhook_disabledWebhook exists but is disabledRe-enable it in the chat’s webhook settings
403ip_not_allowedClient IP is not in the webhook’s IP allowlistAdd your IP in the webhook settings or remove the allowlist
404not_foundWebhook URL is malformed (path doesn’t match /webhooks/v1/{id}/{token})Verify the URL shape
405method_not_allowedUsed a method other than POSTUse POST
429rate_limit_exceededPer-minute or per-hour rate limit hitBack off; the limit resets on the configured window
500send_failedInternal error while delivering the messageRetry with exponential backoff
  • Message size: content is capped at 4000 characters (same cap as bot-sent messages).
  • Default rate limits: 60 messages/minute and 1000 messages/hour per webhook (both configurable at creation/update time; 0 disables either cap).
  • IP allowlist: Optional. Leave empty to accept requests from anywhere. Entries must be exact IP addresses — CIDR ranges are not currently supported and will not match any caller.
  • No attachments: Incoming webhooks send text-only. File uploads require the bot send API or a different flow.
  • No reactions or edits: Webhooks can only post new messages.
  • Bot Send — post messages with a persistent bot identity and bearer-token auth.
  • Bot Receive — receive real-time chat events over WebSocket.