Manage Incoming Webhooks
Provision and manage incoming webhooks over the API instead of the Open API → Incoming Webhooks tab in the CMS. You can create a direct_message webhook for a specific user — resolve them via the Users endpoints first — when you need a standing URL to post to. If you just want to send direct messages from your backend, use the Direct Messages endpoint rather than provisioning a webhook per user.
Required scope: read:incoming_webhooks, write:incoming_webhooks, plus the write scope of the webhook's action to create one or to change its action or config:
| Action | Scope |
|---|---|
chat_message, direct_message | write:chat_messages |
wall_post | write:posts |
calendar_entry | write:calendar_entries |
Without it, create and an update of action or config answer 403 with { "error": "forbidden", "message": "Webhooks with the wall_post action require the write:posts scope." }. Renaming or pausing a webhook does not need it. The trigger URL checks the same scope on every call: while the token that created the webhook lacks it, the call answers 403 (see Incoming Webhooks).
Deleting a webhook, sending a test call and uploading a bot avatar are only available in the CMS. Use revoke to disable a webhook over the API.
Endpoints
/api/open/v1/incoming-webhooksList your incoming webhooks (newest first, not paginated)
/api/open/v1/incoming-webhooks/{id}Get a single incoming webhook
/api/open/v1/incoming-webhooksCreate an incoming webhook
/api/open/v1/incoming-webhooks/{id}Update an incoming webhook
/api/open/v1/incoming-webhooks/{id}/revokeRevoke an incoming webhook (permanent)
Create
curl -X POST https://customer.monotree.com/api/open/v1/incoming-webhooks \
-H "Authorization: Bearer mono_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Reply reminder for Alex",
"action": "direct_message",
"config": { "user_id": 4012 },
"bot_user_id": 873
}'Request body
| Field | Type | Notes |
|---|---|---|
name | string | Required. A label for the webhook, at most 255 characters. If no bot_user_id is given, the bot created for the webhook is named after it. |
action | string | Required. One of chat_message, direct_message, wall_post, calendar_entry. |
config | object | The action's target. Required for direct_message ({ "user_id": ... }), chat_message ({ "room_id": ... }, a group chat: 1:1 chats and AI chats are refused) and wall_post ({ "wall_id": ... }). Leave it out for calendar_entry, which takes no target and returns "config": {}. A target that does not exist returns 422. |
bot_user_id | integer | Optional. The bot user that sends the content; any other user ID returns 422. If omitted, a bot is created automatically. chat_message and direct_message always require a bot. |
use_bot_user | boolean | Optional, default true. Set false to attribute content to the token owner instead of a bot — not allowed for chat_message or direct_message, which answer 422. |
Response
The trigger URL and token are returned once on create and cannot be retrieved again — store them when you receive them.
{
"data": {
"id": 47,
"name": "Reply reminder for Alex",
"action": "direct_message",
"config": { "user_id": 4012 },
"is_active": true,
"use_bot_user": true,
"bot_user": { "id": 873, "name": "Notification Bot", "avatar_url": "https://example.com/bot.png" },
"token_hint": "a1b2",
"last_used_at": null,
"revoked_at": null,
"url": "https://customer.monotree.com/api/open/v1/incoming/xxxxxxxxxxxx",
"token": "xxxxxxxxxxxx",
"created_at": "2026-06-13T10:00:00+02:00"
}
}| Field | Type | Notes |
|---|---|---|
is_active | boolean | false when paused with PUT. A revoke does not change it, so check revoked_at as well. |
last_used_at | datetime or null | When the trigger URL last delivered successfully. null if it never has. |
revoked_at | datetime or null | When the webhook was revoked. A non-null value means the trigger URL is refused. |
GET and PUT responses are the same shape without url and token (those appear only on create).
Update
PUT (or PATCH, which works the same) accepts the same fields as create, plus is_active; only what you send is changed. A config you send replaces the stored one, and changing action to anything but calendar_entry needs the new action's config in the same request. Common uses: rename a webhook, repoint it (config), or pause it with "is_active": false (the trigger URL stops accepting deliveries). You cannot remove the bot from a chat_message or direct_message webhook, or change a webhook without a bot to one of those actions: both answer 422.
curl -X PUT https://customer.monotree.com/api/open/v1/incoming-webhooks/47 \
-H "Authorization: Bearer mono_your_token_here" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'Revoke
POST /incoming-webhooks/{id}/revoke permanently deactivates the webhook: it sets revoked_at and leaves is_active as it was. A webhook only accepts deliveries when is_active is true and revoked_at is null, so after a revoke the trigger URL answers 404. Setting is_active back to true with PUT does not undo a revoke. The record is kept and its delivery logs are kept for 90 days — the API has no hard delete; only the CMS can delete a webhook.