// wiki / webhooks

Webhooks

Configure outgoing webhook notifications for flag events — post straight to Slack or Microsoft Teams, or point at your own endpoint with header/secret authentication and a full delivery log.

Add a webhook

Under Develop → Webhooks, select Add Webhook and give it a destination URL. Every webhook is scoped by three independent choices: the channel it renders for, the events it fires on, and — optionally — a single flag or environment.

Slack
Formatted message

Paste an Incoming Webhook URL from Slack. Events render as a readable block message, no payload wrangling required.

Microsoft Teams
Adaptive Card

Paste a Teams Workflow URL. See Notifications & Teams for the full setup walkthrough.

Generic (custom)
Raw JSON to your endpoint

Point at any HTTPS endpoint you control. Add a signing secret so you can verify each request came from Maxlona.

Authenticate outgoing requests

Two independent mechanisms protect a webhook, and you can use either or both.

Outbound API key

An API key attached to every request this webhook sends, so your receiving server can authenticate the caller. Available on any channel type, and stored keys are never displayed again after creation — only rotated or removed.

How it's sentWhere it landsExample
Custom HTTP headerA header name you chooseX-API-Key: <key>
Authorization: BearerThe standard bearer schemeAuthorization: Bearer <key>
Query string parameterA parameter name you choose?api_key=<key>

Signing secret (Generic channel)

A Generic webhook can also carry a signing secret. When set, every request includes an X-Webhook-Signature header — an HMAC-SHA256 digest of the raw request body — plus X-Webhook-Event naming the event. Recompute the digest with your stored secret and compare before trusting the payload.

Rotate, don't reuse. Rotating the API key or signing secret shows the new value exactly once. Update your receiving server before the next dispatch, or that server will start rejecting requests.

Scope deliveries

Flag and Environment both default to Any, giving you one organization-wide feed. Narrow either dropdown to build a webhook dedicated to a single release or a single environment — useful for routing a noisy flag's changes to its own channel without touching the org-wide feed.

Event types

Select any combination, or every event for a complete operational feed.

AreaEventsUseful for
Flagsflag.created, flag.updated, flag.evaluation_changed, flag.deleted, flag.archived, flag.promoted, flag.unpromoted, flag.toggledRelease visibility, including dependency and automation effects
Approvalsapproval.pending, approval.approved, approval.rejectedReview queues and governance
Kill switchkillswitch.activated, killswitch.clearedIncident response
Billingsubscription.activated, subscription.canceled, payment.succeeded, payment.failedAccount health

Delivery log, retries, and testing

Every dispatch attempt is recorded per webhook, with the request status, HTTP response code, duration, and — for the Generic channel — the exact payload sent. Use Test to send a sample event on demand without waiting for a real change.

StatusMeaning
DeliveredThe endpoint accepted the request.
PendingQueued, or waiting on its next retry attempt.
FailedThe most recent attempt errored; a retry is scheduled if attempts remain.
DeadEvery automatic attempt was exhausted. Replay it manually from the delivery log.

Failed requests retry automatically with exponential backoff, up to five attempts, before being marked dead. A dead or failed delivery can be replayed manually at any time from the deliveries panel — deleting a webhook stops future events but keeps its delivery history.

Payload shape

Every Generic-channel delivery shares the same envelope, regardless of event type — only data changes shape per event.

{
  "eventType": "flag.toggled",
  "timestamp": "2026-09-08T14:32:07Z",
  "data": {
    "flag": "new-payment-screen",
    "environment": "prod",
    "action": "enabled"
  }
}

Slack and Microsoft Teams webhooks receive this same data pre-formatted as a chat message or Adaptive Card instead of raw JSON — nothing further to parse on your end.