Webhook Relays
Overview
Webhook Relays let you forward webhook payloads from external services directly into a Roam group chat, without writing any code. Each relay provides a unique URL that accepts POST requests with a JSON payload and posts the content to your configured recipients.
┌─────────────┐ ┌─────────────────┐ ┌──────────────┐
│ PagerDuty │ │ Webhook Relay │ │ Roam Group │
│ Grafana │──POST──▶│ (unique URL) │───msg──▶│ Chat │
│ Sentry ... │ │ │ │ │
└─────────────┘ └─────────────────┘ └──────────────┘
Your service Templates, filters, Your team sees
sends a webhook and colors applied the notification
This is ideal for receiving notifications from services like PagerDuty, Grafana, Sentry, Datadog, GitHub, or any tool that supports outgoing webhooks.
With Webhook Relays you can:
- Template messages using fields from the incoming payload
- Add buttons that link back to the source service
- Color-code messages to indicate severity or status
- Override colors dynamically based on payload values
- Filter delivery so only matching payloads produce messages
Create a Webhook Relay
- In Roam, navigate to Settings > Developer > Webhook Relays.
- Click Add and enter a name and optional description for the relay.
- After creation, the relay's unique Webhook URL is displayed. Copy it — you'll paste this into your external service.
The URL is a secret. Anyone with the URL can post messages to your chat, so treat it like a credential.
Add Recipients
Under Webhook Message Recipients, add the groups or individuals that should receive relay messages. You can target:
- Groups — Messages appear in the group chat.
- Individuals (by email) — Messages are delivered to a direct conversation.
Configure the Message
The template editor lets you customize how incoming payloads are displayed.
Message Text
Write your message using Markdown. Insert fields from the payload using {{field}} placeholders. Use Insert Field to pick from fields found in the sample payload.
For example, given this payload:
{
"title": "CPU usage above 90%",
"source": "monitor-1",
"url": "https://app.datadoghq.com/monitors/123"
}
A template like:
**{{title}}**
Source: `{{source}}`
produces:
CPU usage above 90% Source:
monitor-1
Nested fields are accessed with dot notation: {{event.data.id}}.
Buttons
Add buttons that appear below the message. Each button has a label and a URL. Button URLs can also use {{field}} placeholders — for example, {{url}} to link back to the alert in your monitoring tool.
Set a Color
Choose a color that renders as a vertical strip on the left side of the message. Pick from presets:
| Preset | Color |
|---|---|
| Good | Green |
| Warning | Yellow / amber |
| Danger | Red |
Or toggle Use Custom Color to enter any hex color (e.g. #5B3FD9).
Color Override Rules
Color overrides let you dynamically change the message color based on payload values. Each rule specifies:
- A field from the payload (e.g.
status) - A match value (e.g.
critical) - A color (Good, Warning, or Danger)
The first matching rule wins. If no rules match, the base color is used.
Example: For an alerting service that sends a severity field:
| Field | Value | Color |
|---|---|---|
severity | critical | Danger |
severity | warning | Warning |
severity | ok | Good |
This way, critical alerts appear with a red strip and resolved alerts appear green — all from the same relay.
Delivery Conditions
By default, every incoming webhook produces a message. Delivery conditions let you filter so that only matching payloads are delivered.
Each condition specifies:
- A field from the payload (e.g.
action) - An operator (see table below)
- One or more values
The available operators are:
| Operator | Description |
|---|---|
| is | Exact match against a single value |
| is not | Negated exact match against a single value |
| is one of | Matches any value in a comma-separated list |
| is not one of | Matches none of the values in a comma-separated list |
| contains | Substring match against a single value |
| does not contain | Negated substring match against a single value |
All conditions must match for the message to be delivered. Payloads that don't match are still captured (updating the sample payload and last-received timestamp) but no message is sent.
Example: Only deliver when severity is one of high, critical:
| Field | Operator | Values |
|---|---|---|
severity | is one of | high, critical |
Example: Only deliver when the message field contains error:
| Field | Operator | Values |
|---|---|---|
message | contains | error |
This prevents low-priority noise from flooding your chat while still capturing all payloads for debugging.
Sender Profile Image
Optionally upload a custom avatar that appears as the sender's profile image for relay messages. This helps distinguish different relays at a glance — for example, using the PagerDuty logo for PagerDuty alerts.
Sample Payload
The first webhook received by a new relay is automatically captured as the sample payload. This sample powers the Insert Field button, which lists available fields and shows example values from the captured payload.
If the payload structure changes (e.g. you switch to a different event type), click Clear to discard the sample. The next incoming webhook will be captured as the new sample.
Connect Your External Service
In your external service's webhook or notification settings, paste the relay's Webhook URL as the destination. The relay accepts POST requests with a JSON body.
Common services with outgoing webhook support:
- PagerDuty — Events & Incidents webhooks
- Grafana — Contact point webhooks
- Sentry — Issue & error alert webhooks
- Datadog — Monitor notification webhooks
- GitHub — Repository webhooks (push, PR, issues, etc.)
- GitLab — Project webhooks
- Jira — Automation webhooks
- Stripe — Event webhooks
Any service that can send a POST request with a JSON body to a URL will work.
Example: PagerDuty Incident Alerts
Here's a complete example setting up a relay for PagerDuty incident notifications.
1. Create a relay named "PagerDuty Alerts" and add your ops group as a recipient.
2. In PagerDuty, add a Generic Webhook (v3) integration and paste the relay URL.
3. Trigger a test incident so PagerDuty sends a payload. The relay captures it as the sample. A PagerDuty payload looks like:
{
"event": {
"event_type": "incident.triggered",
"data": {
"title": "Disk usage above 90%",
"urgency": "high",
"status": "triggered",
"html_url": "https://your-org.pagerduty.com/incidents/Q1EXAMPLE"
}
}
}
4. Configure the message template:
**{{event.data.title}}**
Status: {{event.data.status}} · Urgency: {{event.data.urgency}}
Add a button with label "View in PagerDuty" and URL {{event.data.html_url}}.
5. Add color override rules:
| Field | Value | Color |
|---|---|---|
event.data.status | triggered | Danger |
event.data.status | acknowledged | Warning |
event.data.status | resolved | Good |
6. Add a delivery condition to only deliver high-urgency incidents:
| Field | Operator | Values |
|---|---|---|
event.data.urgency | is one of | high |
Now your ops group receives color-coded PagerDuty alerts for high-urgency incidents, with a direct link back to PagerDuty.
HTTP Response Codes
When an external service posts to a relay URL, the following responses are returned:
| Status | Meaning |
|---|---|
200 OK | Payload accepted and message delivered (or captured if relay is disabled) |
400 Bad Request | Request body is not valid JSON |
404 Not Found | Relay URL does not exist |
413 Payload Too Large | JSON payload exceeds the size limit |
429 Too Many Requests | Rate limit exceeded — retry after the indicated period |