Webhooks & Integrations
Build bots, automations and integrations with mssgs webhooks and triggers
Getting Started
With mssgs webhooks you can send messages to channels and build interactive bots that respond to commands.
Create a webhook
Go to Server Settings > Integrations and create a new webhook or trigger.
Copy the URL
Copy the webhook URL or trigger GUID for your integration.
Start building
Send JSON payloads to the webhook URL to post messages.
Two ways to integrate
Incoming Webhooks — Send messages to mssgs from external tools (CI/CD, monitoring, etc.)
Webhook Triggers — Build interactive bots that respond to commands and button clicks.
Incoming Webhooks
Send messages to a channel via a webhook URL. Perfect for CI/CD notifications, monitoring alerts, and other automations.
/server/:server_guid/webhook/:channel_guid
Request structure
Send a POST request with your message payload as JSON body. Include your server management token in the token field.
{
"token": "server_management_token",
"content": "Hello from my integration!",
"color": "green",
"title": "My Bot"
}
Simple Format
The simplest way to send a message.
{
"content": "Server backup completed at 03:00 UTC",
"color": "green",
"title": "Backup Bot"
}
| Field | Type | Description |
|---|---|---|
content |
string | Message text required |
color |
string | blue, green, orange, red, yellow, purple |
title |
string | Title above the message |
title_url |
string | Makes the title a clickable link |
sub_title |
string | Smaller text below the title |
avatar_url |
string | Avatar image URL |
Full Format (message_container)
For more control over the message, use the full message_container object.
{
"message_container": {
"type": "embed_message",
"color": "blue",
"title": "Order Update",
"description": "Your order #12345 has been shipped!",
"title_url": "https://example.com/orders/12345",
"sub_title": "Estimated delivery: Tomorrow",
"bot_name": "Order Bot",
"fields": [
{ "field": "Status", "value": "Shipped" },
{ "field": "Tracking", "value": "ABC123456" }
]
},
"actions": [...]
}
| Field | Type | Description |
|---|---|---|
type |
string | embed_message (default) or system_message |
color |
string | Accent color of the message |
title |
string | Title of the message |
description |
string | Main text required |
title_url |
string | Link behind the title |
sub_title |
string | Subtitle text |
bot_name |
string | Custom bot name |
avatar_url |
string | Avatar image |
fields |
array | Key-value fields |
Fields
Add structured key-value data to your message.
{
"fields": [
{ "field": "Status", "value": "Completed" },
{ "field": "Duration", "value": "2m 34s" },
{ "field": "Environment", "value": "Production" }
]
}
Webhook Triggers
Webhook triggers let you connect external services to your server. When a user types a matching command (e.g. /help) or clicks an action button, the backend POSTs to your webhook URL. Your webhook responds with JSON to post a message back to the channel.
User types a command
A user sends a message starting with your trigger's prefix, e.g. /help
mssgs calls your webhook
The backend sends a POST request to your configured webhook URL with the message data.
You respond with JSON
Your server returns a JSON response with the message content, embeds and actions.
Message appears in channel
mssgs displays the response as a message in the channel.
Request Your Webhook Receives
When a user sends a message that starts with your trigger's trigger_match prefix:
{your_webhook_url}
{
"server_guid": "abc12345-...",
"channel_guid": "def67890-...",
"trigger_match": "/help",
"message": {
"id": "d01ZZdef6-xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx",
"content": "/help how do I create a channel?",
"member_guid": "member-guid-here",
"user_guid": "user-guid-here",
"cms": 1234567890123,
"group_guids": ["group-guid-1", "group-guid-2"]
},
"callback_url": "https://mss.gs/api/v1/trigger-callback/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Request via Action Button
When triggered via a trigger:{guid} action button, the content is always "[Action Triggered]" and includes the action's payload:
{
"server_guid": "abc12345-...",
"channel_guid": "def67890-...",
"trigger_match": "/help",
"message": {
"id": "d01ZZdef6-xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx",
"content": "[Action Triggered]",
"member_guid": "member-guid-here",
"user_guid": "user-guid-here",
"cms": 1234567890123,
"group_guids": ["group-guid-1", "group-guid-2"],
"is_action_button": true,
"action_payload": {
"custom_key": "custom_value"
}
},
"callback_url": "https://mss.gs/api/v1/trigger-callback/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Request Fields
| Field | Type | Description |
|---|---|---|
server_guid |
string | The server where the trigger was fired |
channel_guid |
string | The channel where the message was sent |
trigger_match |
string | The prefix that matched (e.g. /help) |
message |
object | The original message that triggered the webhook |
message.id |
string | Unique message ID |
message.content |
string | Full message text (command match) or "[Action Triggered]" (action button) |
message.member_guid |
string | The server member who triggered it |
message.user_guid |
string | The user's global GUID |
message.group_guids |
array | The server group GUIDs the member belongs to (use to check roles like admin) |
message.cms |
number | Timestamp in milliseconds |
message.is_action_button |
boolean | true when triggered via an action button (not present for command matches) |
message.action_payload |
object | Custom data from the action button (only present for action triggers) |
callback_url |
string | Unique URL to update or delete the response message (valid for 30 minutes) |
Response Format
Your webhook must respond with 200 status and a JSON body. The response becomes a message in the channel.
Simple response
{
"content": "Hello! How can I help you?"
}
Embed response
{
"message_container": {
"type": "embed_message",
"color": "blue",
"title": "Help",
"description": "Here's how to create a channel...",
"title_url": "https://docs.example.com/channels",
"sub_title": "Channel Guide",
"avatar_url": "https://example.com/bot-avatar.png"
}
}
Full response with actions
{
"content": "Here's what I found:",
"message_container": {
"type": "embed_message",
"color": "green",
"title": "Search Results",
"description": "Found 3 matching items.",
"title_url": "https://example.com/results",
"sub_title": "Query: channels",
"avatar_url": "https://example.com/bot-avatar.png"
},
"actions": [
{
"type": "button",
"text": "View Details",
"color": "blue",
"triggers": [
{
"action": "ws:send",
"payload": {
"method": "USER_REQUESTED_TRIGGER",
"server_guid": "server-guid",
"channel_guid": "channel-guid",
"trigger_guid": "details-trigger-guid",
"action": { "result_id": "42" }
}
}
]
},
{
"text": "Open Docs",
"type": "url:https://docs.example.com"
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
content |
string | Plain text message content |
message_container |
object | Rich embed/system message display |
message_container.type |
string | embed_message (default) or system_message |
message_container.color |
string | blue, green, orange, red (default: blue) |
message_container.title |
string | Embed title |
message_container.description |
string | Embed body text |
message_container.title_url |
string | Makes the title a clickable link |
message_container.sub_title |
string | Smaller text below the title |
message_container.avatar_url |
string | Avatar image URL shown alongside the embed |
actions |
array | Action buttons (see Actions) |
At least one of content, message_container, or actions must be present — otherwise no message is created.
The message author is your trigger's post_app_name (configured in server settings). If not set, the server name is used.
Timeout
Your webhook must respond within 5 seconds. If it times out, an error message is shown to the user. Use the callback URL if you need more time to process.
Actions
Actions are interactive buttons displayed below your message. There are two styles: simple shorthands and trigger chains.
Action Fields
| Field | Type | Description |
|---|---|---|
type |
string | "button" for trigger chains, or shorthand "trigger:{guid}" / "url:{url}" required |
text |
string | Button display text (also accepts label) required |
color |
string | green, blue, purple (primary) or red, orange, yellow (secondary) |
payload |
object | Data passed back when using trigger:{guid} shorthand |
triggers |
array | Sequential trigger chain (for type: "button") |
Simple Shorthands
For basic actions, use the type shorthand:
| Type | Description |
|---|---|
trigger:{guid} |
Fires another webhook trigger by its GUID. The trigger receives message.action_payload with the action's payload. |
url:{url} |
Opens the URL in the user's browser |
{
"actions": [
{
"text": "Check Status",
"type": "trigger:abc123-trigger-guid",
"payload": { "order_id": "12345" }
},
{
"text": "View Order",
"type": "url:https://example.com/orders/12345"
}
]
}
What happens when an action is clicked?
When the user clicks "Check Status", the target trigger's webhook receives a new request with message.content set to "[Action Triggered]", message.is_action_button set to true, and message.action_payload set to { "order_id": "12345" }.
Trigger Chains
For richer interactions, use type: "button" with a triggers array. Triggers execute sequentially and support conditional logic based on the previous trigger's result.
Trigger types
| Action | Description |
|---|---|
ws:send |
Sends a WebSocket command to the gateway. Put the method and payload in payload. |
local:update_message |
Updates the message locally (no server round-trip). Provide message_container and optionally actions. |
local:remove_message |
Removes the message from the client's view. |
Conditions
| Condition | Description |
|---|---|
| (none) | Always executes |
previous:success |
Only if the previous trigger succeeded |
previous:failed |
Only if the previous trigger failed |
Example: Trigger chain with conditions
This button fires a webhook trigger, then updates the message to show a "processing" state on success, or an error on failure:
{
"message_container": {
"title": "Order #12345",
"description": "Your order is being processed."
},
"actions": [
{
"type": "button",
"text": "Check Status",
"color": "green",
"triggers": [
{
"action": "ws:send",
"payload": {
"method": "USER_REQUESTED_TRIGGER",
"server_guid": "server-guid",
"channel_guid": "channel-guid",
"trigger_guid": "status-check-trigger-guid",
"action": { "order_id": "12345" }
}
},
{
"condition": "previous:success",
"action": "local:update_message",
"message_container": {
"color": "blue",
"title": "Order #12345",
"description": "Checking status..."
},
"actions": []
},
{
"condition": "previous:failed",
"action": "local:update_message",
"message_container": {
"color": "red",
"title": "Order #12345",
"description": "Could not check status. Try again later."
}
}
]
},
{
"type": "button",
"text": "Dismiss",
"color": "red",
"triggers": [
{
"action": "local:remove_message"
}
]
}
]
}
What happens when "Check Status" is clicked
1. The client sends USER_REQUESTED_TRIGGER via WebSocket
2. On success: the message updates locally to show "Checking status..." with buttons removed
3. On failure: the message updates to show a red error
4. The webhook fires and its response arrives as a new MESSAGE_CREATE
Rate limit
Users are rate-limited to prevent spam when firing triggers.
Callback URL
Every webhook request includes a callback_url — a unique, token-authenticated URL that your service can call to update or delete the response message after it was posted. The URL is valid for 30 minutes.
Use cases
- Updating a "processing..." message with final results
- Showing live progress (e.g. build status, deployment)
- Removing a message after it's no longer relevant
Update Message
{callback_url}
{
"content": "Updated text content",
"message_container": {
"color": "green",
"title": "Build Complete",
"description": "All 42 tests passed."
},
"actions": []
}
All fields are optional — only include the ones you want to change. To remove action buttons, send "actions": [].
Delete Message
{callback_url}
No request body needed. The message is permanently deleted from the channel.
Responses
// 200 OK
{ "success": true }
// 404 Not Found (callback token expired or invalid)
{ "error": "TOKEN_NOT_FOUND" }
// 400 Bad Request (no update fields provided)
{ "error": "MISSING_FIELDS" }
Example: Progress Updates
// 1. Your webhook responds immediately with a "loading" message
// Response:
{
"message_container": {
"color": "blue",
"title": "Deploying...",
"description": "Starting deployment to production."
}
}
// 2. Your service updates the message as progress continues
// PUT {callback_url}
{
"message_container": {
"color": "blue",
"title": "Deploying...",
"description": "Step 2/3: Running migrations."
}
}
// 3. Final update when done
// PUT {callback_url}
{
"message_container": {
"color": "green",
"title": "Deploy Complete",
"description": "v2.1.0 is now live on production."
},
"actions": [
{
"label": "View Logs",
"type": "url:https://example.com/deploys/123/logs"
}
]
}
Callback URL lifetime
Valid for 30 minutes from when the trigger fires. After that, update/delete requests return 404. Once you delete the message, the callback URL is consumed and cannot be used again.
Errors
When errors occur, you receive a JSON error response.
{
"error": "ERROR_CODE"
}
Error codes
| Code | Description |
|---|---|
INVALID_TOKEN |
Invalid authentication token |
MISSING_REQUIRED_FIELDS |
Required fields are missing in data |
MISSING_CONTENT |
Message requires content or message_container.description |
TOKEN_NOT_FOUND |
Callback token expired or invalid |
MISSING_FIELDS |
No update fields provided in callback request |
UNSUPPORTED_GITHUB_EVENT |
GitHub event type not supported |
UNSUPPORTED_UNIFI_PROTECT_EVENT |
UniFi Protect event not supported |
Success response
{
"success": true
}
Examples
Simple notification
curl -X POST https://mss.gs/server/SERVER_GUID/webhook/CHANNEL_GUID \
-H "Content-Type: application/json" \
-d '{
"token": "your_token",
"content": "Build completed successfully!"
}'
Colored alert with link
{
"token": "your_token",
"content": "Build #123 completed!",
"color": "green",
"title": "CI/CD Pipeline",
"title_url": "https://github.com/org/repo/actions/runs/123"
}
Error alert
{
"token": "your_token",
"message_container": {
"type": "system_message",
"color": "red",
"title": "Alert: Database Error",
"description": "Connection failed: timeout after 30s",
"sub_title": "prod-db-01"
}
}
Deployment approval with actions
{
"token": "your_token",
"message_container": {
"color": "yellow",
"title": "Deployment Request",
"description": "User @johndoe requested a deployment to production."
},
"actions": [
{
"label": "Approve",
"type": "trigger:approve-deploy-trigger-guid",
"payload": { "deploy_id": "dep_123", "env": "production" }
},
{
"label": "Reject",
"type": "trigger:reject-deploy-trigger-guid",
"payload": { "deploy_id": "dep_123" }
},
{
"label": "View Changes",
"type": "url:https://github.com/org/repo/compare/main...deploy"
}
]
}
Trigger response: private message
{
"message_container": {
"description": "This is a private response only you can see."
},
"visible_to_member_guids": ["<member_guid from request>"],
"ephemeral": true
}
Trigger response: interactive menu
{
"message_container": {
"title": "What would you like to do?",
"description": "Choose an option below:"
},
"actions": [
{
"label": "Get Help",
"type": "trigger:help-trigger-guid"
},
{
"label": "View Stats",
"type": "trigger:stats-trigger-guid",
"payload": { "period": "weekly" }
}
]
}
Special Webhooks
mssgs automatically recognizes certain webhook types.
GitHub Webhooks
Automatically detected via the x-github-event header. Supported events: Push, Pull Requests, Issues, Releases, and more.
UniFi Protect Webhooks
Automatically detected via user-agent: protect-alarm-manager header.
Notes
- Timeout: Your webhook must respond within 5 seconds. If it times out, an error message is shown to the user. Use the callback URL if you need more time to process.
- Message persistence: Trigger response messages are saved to the database and appear in channel history.
- Callback URL lifetime: Valid for 30 minutes from when the trigger fires. After that, update/delete requests return
404. - Rate limiting: Users are rate-limited to prevent spam when firing triggers.
Questions?
We're happy to help you with your integration.
developers@mss.gs