title: Webhooks API Reference
shell: standard
components:
- type: breadcrumb
items:
- label: Home
href: ../index.html
- label: API Reference
href: "#"
- label: Webhooks
- type: header
title: Webhooks API
eyebrow: API Reference
subtitle: Receive real-time event notifications from your account.
- type: meta
fields:
- key: Version
value: "2.4"
- key: Status
value: Stable
- key: Last Updated
value: 2026-04-14
- key: SLA
value: 99.95%
- type: markdown
body: |
Webhooks push events to a URL you configure. Use them to keep external systems
in sync with state changes in your account — new users, payment events, subscription
changes, and more.
- type: section
eyebrow: Authentication
heading: Signing requests
components:
- type: markdown
body: |
Every webhook request includes an `X-Signature` header containing an
HMAC-SHA256 of the raw request body, keyed by your webhook secret.
Verify this signature before processing any event.
- type: code
language: python
code: |
import hmac, hashlib
def verify(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
- type: section
eyebrow: Event types
heading: Supported events
components:
- type: table
filterable: true
columns:
- key: name
label: Event
sortable: true
- key: since
label: Since
sortable: true
- key: retries
label: Retries
sortable: true
align: right
- key: status
label: Status
rows:
- name: user.created
since: v1.0
retries: 3
status: GA
- name: user.deleted
since: v1.0
retries: 3
status: GA
- name: invoice.paid
since: v1.2
retries: 5
status: GA
- name: invoice.failed
since: v1.2
retries: 5
status: GA
- name: subscription.upgraded
since: v2.0
retries: 3
status: GA
- name: subscription.canceled
since: v2.0
retries: 3
status: GA
- name: trial.ending
since: v2.4
retries: 1
status: Beta
- type: section
eyebrow: Examples
heading: Integration snippets
components:
- type: tabs
tabs:
- label: Express.js
components:
- type: code
language: javascript
code: |
app.post('/webhook', (req, res) => {
if (!verify(req.rawBody, req.headers['x-signature'], SECRET)) {
return res.status(401).end();
}
const event = req.body;
handlers[event.type]?.(event.data);
res.status(200).end();
});
- label: Flask
components:
- type: code
language: python
code: |
@app.route('/webhook', methods=['POST'])
def webhook():
if not verify(request.data, request.headers['X-Signature'], SECRET):
return '', 401
event = request.json
handlers.get(event['type'], noop)(event['data'])
return '', 200
- label: Go
components:
- type: code
language: go
code: |
func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
if !verify(body, r.Header.Get("X-Signature"), secret) {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
var event Event
json.Unmarshal(body, &event)
handle(event)
w.WriteHeader(http.StatusOK)
}
- type: callout
variant: warn
title: Idempotency
body: |
Deliveries may retry on failure. Use the `event.id` field as an idempotency key —
we guarantee it's unique across all deliveries of the same event.
- type: callout
variant: info
title: Next up
body: Other example pages — KBs, decks, landing pages, meeting briefs, API references, dashboards from live data.
links:
- label: See all use cases
href: ../about.html
variant: primary
- label: Components reference
href: ../components/index.html
variant: secondary