Webhooks
Send Dirstarter domain events to your own automation (n8n, Zapier, Make) with outbound webhooks
Dirstarter emits domain events (a tool is submitted, a tool changes status, a paid tier is upgraded, a report comes in) and can fan them out to two outlets: transactional emails and an optional outbound webhook. The webhook lets you drive your own automation (n8n, Zapier, Make, or any HTTP endpoint) whenever something happens in your directory.
Webhooks are off by default. Set WEBHOOK_URL and every event starts POSTing to it; leave it unset and the feature is a silent no-op.
Setup
Point WEBHOOK_URL at your receiver. An optional WEBHOOK_TOKEN is sent as a Bearer header so the receiver can verify the request came from you.
WEBHOOK_URL="https://n8n.example.com/webhook/dirstarter"
WEBHOOK_TOKEN="a-shared-secret" # OptionalDelivery runs after the response is sent (after()), so it never blocks the request. Errors are logged and swallowed: there is no queue or retry, since the mutation has already committed.
Payload
Each event is delivered as a small JSON envelope. The data payload is intentionally flat: ids, enums and scalars, never a serialized model. A receiver that needs full detail looks it up by id, which is exactly how a real automation (n8n / Zapier) works.
{
"id": "clx0abc123...",
"event": "tool.status_changed",
"site": "your-directory",
"timestamp": "2026-08-27T10:00:00.000Z",
"data": {
"toolId": "clx0tool456...",
"previousStatus": "Draft",
"newStatus": "Published",
"notifySubmitter": true
}
}The id is unique per delivery, so a receiver can dedupe. When WEBHOOK_TOKEN is set, the request carries Authorization: Bearer <token>.
Events
| Event | Fires when |
|---|---|
tool.submitted | A user submits a tool |
tool.status_changed | A tool's status changes, including admin create, admin edit, and the publish cron. previousStatus is null when the tool was created straight into its status |
tool.tier_upgraded | A tool is upgraded to a paid tier via Stripe |
tool.tier_downgraded | A paid tool is downgraded to Free |
report.created | A user reports a tool |
post.status_changed | A blog post's status changes, including create and the publish cron |
To key off publishing (e.g. share newly published tools on socials), listen for tool.status_changed where newStatus is Published. It catches admin publishes, create-as-published, and the cron alike.
Extending
Everything lives in server/events.ts. Adding an event is one line in the EventMap plus a sendEvent("your.event", { ... }) call at the emit site. Adding an email for it is one case in server/event-emails.ts; adding another outlet (Slack, Discord) is one function in the Promise.allSettled array.
Last updated on