Webhooks
A page for administrators. Webhooks are two independent event channels: inbound (an external system calls TaskFlow) and outbound (TaskFlow notifies the outside world). They live in different sections and are unrelated to each other.
| Direction | Section | Who calls whom |
|---|---|---|
| Inbound | Admin → Webhooks | External system → TaskFlow |
| Outbound | Admin → Outbound calls | TaskFlow → external system |
Inbound webhooks
Admin → Webhooks hands an external system a secret address. Everything that arrives there is verified, written to a log and turned into an internal event — which automation rules and product features subscribe to.
Receiving and handling are separate
This page only covers receiving: who is allowed in, how the sender is verified, what arrived. What happens to the event — create an issue, add a comment, trigger another rule — is decided by an automation rule. That is why a single hook serves any number of scenarios: several rules can listen to it.
Creating a hook
Add hook opens the form:
| Field | What it sets |
|---|---|
| Name | Lowercase letters, digits and dashes. The name becomes part of the event name (taskflow.hook.<name>) that rules refer to, and cannot be changed later. |
| Title | A human-readable label in the list. Optional. |
| Sender verification | How to be sure the request came from the party you handed the address to — see below. |
| Shared secret | Appears for two of the three verification modes. The same secret is entered on the sender's side. |
| Signature header | Signature mode only: which header carries the signature (x-hub-signature-256 for GitHub). |
| Delivery id header | If the sender numbers its deliveries (x-github-delivery), a repeated delivery will not produce a second event. Empty — no duplicate protection. |
Once created, the page shows the ready-made address, of the form https://<your-host>/hooks/<token>. That is what goes into the external system's settings.
The address is shown once
Only its fingerprint is stored, so there is no “look it up later”. Copy the address immediately; once you close the card, it is gone. Lost it — do not hunt for it, press Rotate token: the old address stops working instantly and the new one has to be given to the sender.
Sender verification
A secret address is already a pass, but not every scenario is satisfied with it. There are three modes:
| Mode | When to pick it | What it protects |
|---|---|---|
| Token in the URL only | The sender can only “POST to a URL”: Slack, n8n, Jira Automation | The address, not the content: whoever learns the address can send anything. |
| Body signature (HMAC-SHA256) | GitHub, Stripe, Shopify and anyone who signs the body | The only mode where the body cannot be tampered with in transit. |
| Bearer header | The sender cannot sign but can set a header (GitLab) | A secret in a header; the body is not signed. |
Choose the signature wherever the sender supports it. A hook that needs a secret but has none is flagged in the list — such a hook must not let anyone in.
The hook list
The table shows, for each hook: title and name, verification mode, the event name it produces, the time of the last call and its state. Actions:
- Rotate token — a new address; the old one dies immediately.
- Disable / Enable — receiving stops, settings are kept. A disabled hook answers the sender with a refusal rather than with silence.
- Delete — the hook is gone, its call log stays: the history of what was received is not rewritten.
A hook created not by you but by a product feature carries that feature's name. Such a hook only works while the feature itself is enabled.
The call log
The lower block is the log: time, hook, the response sent back, result code, body size and source address. Filters — by hook, by result (accepted / rejected) and page size. Rejected attempts are recorded too — otherwise “the webhook never arrives” would look like complete silence.
Show expands the headers and body of the request — which is the main reason the log exists: without a real body you cannot write a rule. Headers carrying secrets (Authorization, the signature header) are stripped from the log. The log keeps 14 days.
Result codes read directly:
| Code | What happened |
|---|---|
HOOK_ACCEPTED | Accepted, an event was created. |
HOOK_UNKNOWN_TOKEN | The address matches no hook: a typo, or a rotated token. |
HOOK_DISABLED | The hook is disabled — or the feature that owns it is. |
HOOK_BAD_SIGNATURE | Signature or Bearer did not match: different secrets on the two sides. |
HOOK_BODY_TOO_LARGE | The body exceeded the allowed size. |
HOOK_DUPLICATE | A repeated delivery with the same id — no second event was created. |
From a call to an action
- Create the hook and paste the issued address into the external system.
- Let it send a real event, and inspect its body in the log.
- Build an automation rule triggered by that hook's event, with conditions on the body's fields.
- Check the rule with a dry run, then enable it.
The sender got a 200 — and nothing happened
That is the intended split of responsibility: receiving worked, so the rest is up to the rule. Look at the automation run log, not at the call log. And the other way round: if there is no entry in the call log at all, the request never reached the instance — check the address and the network on the sender's side.
Hooks that features create themselves
Some features need an inbound webhook to work and create one on their own; such a hook appears in the same list marked with its owner:
- Dev links — receive push events from GitHub, GitLab and Bitbucket to show branches and commits on the issue card; see Linking issues to your git hosting.
- Mail via JMAP — receives the mail server's notification about a new message; see Email → Receiving mail.
Do not delete such hooks by hand: the feature will create them again, and the external system will need a new address.
Outbound webhooks
Admin → Outbound calls is the other direction: TaskFlow itself POSTs to an external system when something happens.
Each call defines:
| Field | What it sets |
|---|---|
| Name | A label in the list — “Slack notifications”, say. |
| URL | Where to send. |
| Secret | Shared secret for signing the body (HMAC-SHA256) so the receiving side can verify the sender. Left empty when editing, it means “keep the current one”. |
| Events | Comma-separated: which events to react to. At least one. |
| Active | A switch that does not delete the setting. |
Test sends a probe request and shows the result immediately; Deliveries opens the log for that particular call — event, time and outcome. An automation rule action can call an external address too.
Internal addresses are blocked
Requests to internal network addresses (localhost, private ranges, cloud metadata endpoints) are refused. This prevents a webhook from being used to probe your infrastructure, and it cannot be configured away.
Next
- Automation — the rules that handle received events.
- Plugins — enabling the features mentioned above.
- AI & API access — other ways to connect TaskFlow to external systems.
See also
- Email → Receiving mail — mail intake runs on an inbound hook just like these.
- AI & API access → Link issues to your git hosting — a worked example built on an inbound webhook.
- Features & settings → Automation, webhooks and notifications — where both directions sit in the wider picture.
- Configuration → Required in production —
APP_BASE_URL, which the hook address is built from.