Webhooks
All team-scoped mutations can fire a webhook to the team's configured
webhook_url (set in Settings → General). Delivery is fire-and-forget: the
originating request never blocks on the webhook, and failures are silently
ignored. The pattern is general — add fireWebhook(...) anywhere a mutation
happens (announcements, profile updates, custom feature events) and external
tools can react.
Payloads always include event, team_id, team_name, and timestamp;
event-specific fields are listed below.
Built-in events
| Event | Fired when | Extra fields |
|---|---|---|
invitation.created | Admin invites someone | email, role, invite_url, invited_by |
invitation.accepted | Invitee accepts the invitation | email, role, member_name |
invitation.revoked | Admin revokes a pending invitation | email, role, revoked_by |
member.removed | Admin removes a member | member_email, member_name, removed_by |
member.role_changed | Admin changes a member's role | member_email, member_name, old_role, new_role, changed_by |
team.updated | Team settings are saved | changes (object of updated fields), updated_by |
team.deleted | Owner deletes a team | deleted_by |
Adding your own events
fireWebhook lives in server/utils/fireWebhook.ts. Two steps to add a new
event type:
- Extend the
WebhookEventunion infireWebhook.tswith your event name (e.g."announcement.published"). - Call it from your server route after a mutation:
await fireWebhook( serviceClient, teamId, "announcement.published", { announcement_id: id, title, published_by: user.sub }, );
Natural extensions:
announcement.publishedafter creating a bannerprofile.updatedwhen a user changes their display name or avatar<resource>.created/<resource>.deletedfor any feature you build on top of the template
Reliability
Webhook delivery is fire-and-forget: failures are silently ignored and do not
block the originating request. If you need retries or delivery guarantees,
replace server/utils/fireWebhook.ts with a queue-backed implementation
(BullMQ, Inngest, a Postgres job table, etc.).