Managing Sources
The Sources page lets you create and manage webhook endpoints that receive incoming events.
Creating a Source
- Click Create Source
- Enter a name for the endpoint
- Optionally set an event type path (dot-notation path to extract event type from the body)
- Choose an auth method: signing secret (default), API key, or None (Public)
- Optionally add an IP allowlist
- Click Create
Dispatch generates a unique slug and signing secret. The post-create screen shows your webhook URL with a copy button, alongside the one-time signing secret or API-key instructions, so you can paste it straight into the sending service. Your webhook URL is:
https://your-api.com/hooks/<slug>
Source Configuration
Signing Secret
The signing secret is shown once at creation. Use it to sign outgoing requests from your external service. You can regenerate the secret at any time, but the old secret immediately becomes invalid.
Public (unauthenticated) sources
Choose None (Public) as the auth method when the sending service can't sign its payload or send an Authorization header — for example a status-page subscription. The endpoint then accepts requests with no signature and no API key.
Because there's no secret, the source URL itself is the credential: its slug contains an unguessable token. Sources created as public get a fully opaque slug that doesn't include the source's name; a source switched to public later keeps its existing URL, so the sender you've already configured keeps working. Keep the URL private — anyone who has it can send events. Public sources are marked with a Public badge on the Sources page. For extra protection, add an IP allowlist.
Changing the auth method later
You don't have to recreate a source to change how it authenticates. Open Edit Source on a custom source (provider-connected sources are managed through their integration) and use the Authentication selector to switch between:
- Signing Secret — generates a new secret (shown once) and requires a signature on every request.
- API Key — removes the signing secret and requires a project API key instead.
- Public — removes the signing secret so an unsigned sender, like a status page, stops being rejected.
Each switch is confirmed before it applies (it creates or removes credentials), and takes effect on incoming webhooks right away. The slug and your webhook URL stay the same.
IP allowlist
Optionally restrict which IP addresses can deliver to a source. Enter one IP address or CIDR range per line (e.g. 203.0.113.7, 198.51.100.0/24). When set, requests from any other address are rejected. Leave it blank to allow all addresses. You can change the allowlist later from the source's edit dialog.
Event Type Path
Set a dot-notation path (e.g., action or event.type) to automatically extract the event type from the request body. This is useful when the sending service doesn't include an event type header.
Typing Rules
When neither a provider header, the configured event type header/path, nor the generic X-Event-Type header identifies an event, typing rules can derive a type from the payload itself — so a status page that sends an incident object can still store incident_scheduled_maintenance instead of nothing.
Open typing rules from a custom source's edit dialog, or click Derive a type on any untyped event's detail page — that link works for every source type. Each rule (up to 10 per source) has:
- Conditions — the same grammar as routing rules (
body.andheader.fields, operators likeexists,equals,matches regex, combined with AND/OR). - Parts — an ordered list of literals and payload paths assembled into the type. The result is downcased, runs of other characters become single underscores, and parts join with underscores: literal
incident+ pathbody.page.status_descriptionstoresincident_scheduled_maintenance.
Field boxes suggest paths and header names from the source's recent events (the last 30 days) — and arriving via Derive a type also grounds the suggestions on the exact event you came from, so you can pick from its fields instead of retyping them. When there is nothing to suggest, a note on the page says so.
You can also describe the rules you want in plain English: click Generate rules with AI — the sparkle button next to Add rule, the button shown when the list is empty, or Ctrl+Alt+P (⌘⌥P on macOS) — and describe the outcome (for example "type incidents as incident_ plus their status, and anything with a deploy object as deploy"), then press Generate (Ctrl+Enter — ⌘Enter on macOS — works too). Generation reads a handful of the source's recent events so paths match your payloads, sees your existing rules so new ones complement rather than repeat them, and shows the result in a review pane — Apply appends the rules, fully editable, and nothing is stored until you save. When you arrive via Derive a type, the event you came from is included in the generation too, even if it's older than 30 days. Press Ctrl+S (⌘S on macOS) to save; you stay on the page, and a Saved check beside the button confirms your rules are stored until your next change clears it.
Rules evaluate top to bottom at ingest; the first rule that matches and derives a non-empty value stamps the event — so order is meaning here too: use the up and down arrows on a rule, or focus one and press Alt + ↑ / ↓ (⌥↑ / ⌥↓ on macOS) to reorder. A derived type behaves like any other — it shows in tables and pickers, and destination event-type filters apply to it. The same is true of types from the X-Event-Type header, which is honored automatically: if your sender already includes it, its events gain a type without any configuration, and destinations that filter by event type will apply their lists to that stream. Rules affect new events only; already-stored events keep their type.
Active/Inactive Toggle
Deactivate a source to stop accepting incoming webhooks. The endpoint URL returns 404 when inactive. Reactivate at any time to resume.
Deleting and Archiving
Click the trash icon on a source to remove it. Dispatch chooses one of two outcomes automatically so your billing and event history stays intact:
- Permanent delete when the source has never received an event. The source and its webhook URL are removed from the database with no record left behind.
- Archive when any events have been received. The source is hidden from the active list, its webhook URL starts returning
410 Goneto anyone still calling it, and its events stay in place so they remain countable for billing and visible on the Events page.
Archived sources show up under the Archived option in the status filter on the Sources page. Click Restore on any archived source to re-enable its webhook URL — its existing events, filters, and destination links are preserved.
Linking Destinations
From the Sources page, link destinations to an endpoint:
- Click on a source to expand it
- Click Link Destination
- Select a destination from the dropdown
- Click Link
Once linked, the destination appears under the endpoint. Click the routing icon (branch icon) on any linked destination to open its routing page — a full-page editor for everything that link does with an event.
Link Configuration
The routing page has its own URL, so you can bookmark or share a link's configuration. Each endpoint-destination link has three layers of configuration, all managed there:
Default Template
Select a message template to apply to all events delivered to this destination. If no routing rule matches, the default template is used. Leave blank to use the platform's built-in embed format.
Chat destinations only: webhook destinations always receive the raw payload, so webhook links have no template setting — and a template previously saved on a webhook link is cleared the next time the link is saved.
Default Transform
Select a named JSONata transform to reshape the payload before it reaches the formatter. Applied when no routing rule matches. Webhook destinations only — for chat destinations (Discord, Slack, Telegram) transforms run before formatting.
Default Thread (Discord only)
For Discord destinations, you can set a default thread on the link. When set, all events delivered to that destination are posted to that thread by default. Individual routing rules can override this with their own thread.
For OAuth-connected destinations, the routing page shows editors a searchable thread picker grouped by channel; members with view-only access see the thread ID instead. For destinations added by webhook URL, enter the thread ID manually. See Thread Support.
Default Custom Headers
For webhook destinations only. Key-value pairs added to every outgoing HTTP request. These are overridden per routing rule when a rule specifies its own headers.
Routing Rules
Routing rules let you define conditional logic per link: deliver with a specific template and transform, or skip delivery entirely based on event payload or header values.
Rules are evaluated top-to-bottom. The first matching rule wins. If no rule matches, the link's defaults apply. A matched rule applies its own template and transform settings outright — a matched rule with neither set delivers with the plain default format, replacing the link's defaults rather than inheriting them. The rule editor labels each override a delivering rule leaves unset (a default format or no transform badge, plus an inline note), so a rule you added just for a delay, headers, or a transform doesn't silently drop your template. Webhook destinations always deliver the raw payload — templates don't apply to them — so their links offer no template settings at all, and these unset-override labels cover the transform side only there.
A link can hold up to 50 routing rules, and each rule up to 50 conditions and 50 custom headers. Rule labels, condition fields and values, header names and values, and Discord thread IDs and names each have a maximum length; the editor stops you before you exceed one.
Reading the rule list
Every rule starts collapsed, so a long rule set stays scannable. A collapsed row shows the rule's name, what it matches on — a condition count with its AND/OR logic, or the named filter it uses — whether it delivers or skips, and a badge for each override it applies (template, transform, headers, delay, thread). A rule that matches using a named filter shows Per filter instead of Deliver or Skip, because the filter’s own allow/block action decides that. Click a row to expand it and edit.
Reordering rules
Because the first matching rule wins, order is meaning. Use the up and down arrows on a rule, or focus a rule and press Alt + ↑ / ↓ (⌥↑ / ⌥↓ on macOS). With a rule focused, ↑ and ↓ on their own move between rules, and Enter or Space expands and collapses the one you're on — so a whole rule set can be built and reordered without a mouse. Press Ctrl+S (⌘S on macOS) to save; you stay on the page, and a Saved check beside the button confirms your rules are stored until your next change clears it.
Ctrl+S here — and Ctrl+R in the Test panel below — are default bindings; any shortcut can be rebound or unbound. The arrows, Enter, and Space belong to the rule list itself and aren't rebindable.
Adding a Rule
Click Add Rule — the new rule opens expanded — then configure:
| Field | Description |
|---|---|
| Label | Optional name shown on the collapsed row, e.g. "Merged PRs only". Leave it blank and the rule is listed by its position |
| Conditions | One or more field conditions to match (see below), or a reusable named filter |
| Logic | AND — all conditions must match; OR — any condition can match |
| Action | Deliver — send the event (with optional overrides); Skip — drop the event for this destination only |
A rule that matches using a named filter takes its deliver-or-skip decision from that filter's own action, so it has no separate Action setting.
Conditions
Each condition specifies a field path, an operator, and (for most operators) a value:
| Field path format | Example |
|---|---|
body.<path> | body.action, body.pull_request.merged |
header.<name> | header.x-github-event, header.x-event-type |
Once a source has received events, the field box suggests paths and header names taken from its recent events (the last 30 days), so you can pick a path instead of typing it from memory. With no events in that window the box is plain text, and a note above the rule list says suggestions are unavailable. Below each body condition, a small preview shows the shape of payload the condition is looking for.
| Operator | Matches when… |
|---|---|
eq | field equals value |
neq | field does not equal value |
contains | field contains value (substring) |
not_contains | field does not contain value |
includes | field equals one of a comma-separated list of values |
exists | field is present (no value needed) |
not_exists | field is absent (no value needed) |
regex | field matches a regular expression |
If the field is missing from the event, only not_exists matches — every other operator (including neq and not_contains) does not. The exception is wildcard (*) paths, where negative operators still match when the array is missing or empty.
regex patterns use RE2 syntax: character classes, anchors, groups and repetition all work, but backreferences and lookaround are not supported. A pattern RE2 can't read never matches anything, which is easy to miss — use the test panel below to catch an invalid pattern before you save it.
Deliver Action — Overrides
When the action is Deliver, you can optionally override the link defaults for this rule only:
- Template — use a different template for matching events (chat destinations only)
- Transform — use a different transform for matching events
- Custom Headers — use different HTTP headers (webhook destinations only)
- Delay — wait a different number of seconds before delivering matching events
- Thread ID — post to a specific Discord thread for matching events (Discord destinations only)
Template and transform apply outright on a match: leaving one blank delivers with the plain default for that setting, replacing the link's default rather than inheriting it — the editor labels this on the rule. Custom headers add on top of the link's default headers, and a blank delay or thread falls back to the link's default.
Skip Action
When the action is Skip, the event is dropped for this destination — no delivery attempt is made. Skipped events are still delivered to other linked destinations that don't skip them. Template, transform, and header fields are ignored when skipping.
Example
Link: GitHub → #deployments (Discord)
Default template: "GitHub Default"
Rules:
1. body.action = "closed" AND body.pull_request.merged = "true"
→ Deliver with template "Merged PR"
2. body.ref contains "refs/heads/main"
→ Deliver with template "Main Branch Push"
3. header.x-github-event = "ping"
→ Skip
A ping event is dropped. A merged PR delivers with "Merged PR". A push to main uses "Main Branch Push". Everything else uses "GitHub Default".
Generating rules with AI
You can also describe your routing in plain English and let Dispatch draft the rules. Click Generate rules with AI — the sparkle button next to Add Rule, the button shown when the list is empty, or Ctrl+Alt+P (⌘⌥P on macOS) — describe what should happen (for example "skip draft pull requests, and use the Release template for releases"), and press Generate (Ctrl+Enter — ⌘Enter on macOS — works too).
Generation reads a handful of the source's recent events (from the last 30 days) so conditions reference fields your payloads actually have — the dialog says how many events it's working from, and with none it works from your description alone. It also sees your existing rules, so new ones complement rather than repeat them. A generated rule can set a label, conditions, skip, and a template or transform from your project; delays, custom headers, and thread routing are always left for you to configure by hand.
The rules appear in a review pane before anything changes — each regex is checked automatically, so an invalid pattern is flagged right there — and Apply adds them after your existing rules, expanded and fully editable. Nothing is stored until you save the page, so you can adjust or remove anything the AI got wrong first.
Testing your rules
Rules are evaluated top to bottom and only the first match wins, so it isn't always obvious which one an event will land on. The Test panel under the rule list answers that without saving anything and without sending a delivery.
- Paste a sample payload, or pick one of the source's recent events from Use a recent event — type in it to search the newest 100 by event type or id (events without a type show a short id instead of all looking alike, and events your filters blocked are marked filtered); picking one fills in both the payload and its headers
- Optionally edit the headers
- Click Run, or press
Ctrl+R(⌘Ron macOS) from anywhere on the page
The panel tests the rules exactly as they are on screen, including edits you haven't saved yet. It reports:
- which rule matched — by position and label — or that no rule matched and the link defaults apply
- what the winning rule does: the template, transform, headers, delay and thread it applies. Note that a matching rule with no template selected resets the template to the default embed format rather than falling back to the link's default — the same is true for transforms
- a clear callout when the winning rule skips delivery
- why each rule did or didn't match: every condition, the value found at its field path (or that the field was absent), and whether it matched
- rules that were never evaluated because an earlier rule already won, and rules that can never match because they have no conditions
A rule that takes its conditions from a reusable filter is marked as such. You get one verdict per filter condition — the value found at its field path and whether it matched — rather than the conditions themselves, which are defined on the filter's own page. If the filter is empty, the rule is called out as never matching, naming the filter rather than the rule.
Anything the engine couldn't evaluate is flagged in place: an invalid regex pattern, a pattern longer than 1000 characters, an unknown operator, a field path that starts with neither body. nor header., or a reusable filter a rule points at that is no longer available. An invalid pattern is reported even when the condition's field is missing from the sample payload — which is exactly when you'd otherwise never notice it. These are advisory — they explain a result, and they never block saving.
Viewers can run the test panel too: it only evaluates, it changes nothing.
Filters
Filters run at the endpoint level before any delivery. They control which events are accepted at all — a blocked event is never delivered to any destination.
- Click Filters on the source
- Click Add Filter Group
- Choose an action:
- Allow — only events matching this group pass through
- Block — events matching this group are dropped immediately
- Set the logic (AND or OR) and add conditions
- Enable/disable groups without deleting them
Reusable Filters
You can also attach project-level named filters to a source instead of (or alongside) inline filter groups:
- Click Attach Filter in the Filters section.
- Select a named filter from the project's filter list.
- Use the toggle to enable or disable an attached filter per source without detaching it.
- Click the detach button to remove the link entirely.
Named filters are managed from the Filters sidebar page. See Filters for details.
Evaluation Order
Block groups are checked first (inline and attached). If any enabled block group matches, the event is dropped and recorded as filtered. Allow groups are checked next: if any enabled allow groups exist, the event must match at least one of them to proceed. If no allow groups are configured, all non-blocked events pass.
Use Filters when you want to suppress events entirely. Use Routing Rules when you want per-destination control or want to swap templates and transforms conditionally.
See Filters for a full reference of operators and field paths.
Integrations
Use the integration buttons to connect external services directly. Each integration handles OAuth authorization, endpoint creation, and webhook registration automatically. You can also choose which event types to receive when connecting.
| Integration | Description |
|---|---|
| GitHub | Connect a GitHub repository. Dispatch registers a webhook on the repo and auto-formats push, pull_request, issues, and other GitHub events. |
| GitLab | Connect a GitLab project via OAuth. Dispatch registers a webhook and auto-formats push, merge request, and issue events. |
| Bitbucket | Connect a Bitbucket repository via OAuth. Dispatch registers a webhook and auto-formats push and pull request events. |
| Slack | Connect a Slack workspace. Dispatch creates an app endpoint to receive Slack event callbacks. |
| Jira | Connect a Jira project via OAuth. Dispatch registers a webhook and auto-formats issue created, updated, and deleted events. |
| Linear | Connect a Linear workspace via OAuth. Dispatch registers a webhook and auto-formats issue and project events. |
| Stripe | Connect a Stripe account. Dispatch registers a webhook endpoint and auto-formats charge, payment_intent, subscription, and invoice events. |
| PayPal | Connect a PayPal account. Enter your PayPal Webhook ID and register the generated URL in the PayPal Developer Dashboard to receive payment, subscription, and dispute events. |
| Confluence | Connect a Confluence space via OAuth. Dispatch registers a webhook and receives page, blog, and space events. |
For detailed setup steps for each integration, see the Integrations section.
Testing
Click Send Test on any source to send a synthetic event through the endpoint. You can provide a custom payload or use the default test payload {"test": true, "message": "Hello from Dispatch!"}.