Developer Docs
Webhooks
Webhooks are signed notifications that tell you something changed. Fetch the latest note content from the REST API.
Webhook events
| Event | Fires when |
|---|---|
| note.ended | A recording session has finished and its final transcript is available through the API. This event fires once per recording session, so a note with multiple sessions can emit it more than once. |
| note.summary.generated | A summary has finished generating and is available through the API. |
| note.updated | An API-visible field, such as the title or content, changes after the note has ended. Several edits made close together are combined into one event. |
| note.deleted | The note is deleted (data.reason: "deleted") or leaves the API key's visibility scope (data.reason: "access_lost"). |
Two events describe the webhook endpoint itself. endpoint.verification is sent when you create an endpoint or change its URL; respond with a 2xx to activate the endpoint. endpoint.test is sent when you select "Send test" in the console or call the test API.
Request body
A webhook request contains identifiers and processing statuses, not the transcript or summary itself. After receiving the notification, use note_id to fetch the latest content from the REST API.
{
"event_id": "5f8c0a5e-...",
"event_type": "note.ended",
"occurred_at": "2026-08-12T05:20:00Z",
"data": {
"note_id": "note_abc123",
"recording_session_id": "rec_456",
"revision": 7,
"transcript_status": "ready",
"summary_status": "pending"
}
}Verify signatures
Alt signs each request with the whsec_... secret issued when the endpoint was created. The signature follows the Standard Webhooks specification:
POST /webhooks/alt HTTP/1.1
Content-Type: application/json
webhook-id: 5f8c0a5e-...
webhook-timestamp: 1765515600
webhook-signature: v1,K5oZfzN95Z9UVu1EsPQmSmZQGGVfCM0jaZ0lPI4dvLU=signed_content = "{webhook-id}.{webhook-timestamp}.{raw request body}"
signature = base64( HMAC-SHA256( base64url_decode(secret_after_whsec_), signed_content ) )- Verify the signature against the raw request body before your framework parses the JSON. Re-serializing parsed JSON can change the body and break verification.
- Use a constant-time comparison to prevent timing attacks.
- Reject stale timestamps to prevent replay attacks. We recommend accepting timestamps within ยฑ5 minutes of the current time.
- Reject requests whose signature does not match with a 4xx. Working receiver code: Quickstart step 4.
Delivery failures and retries
- Respond with a 2xx within 10 seconds. Any other status or a timeout counts as a delivery failure. Respond first, then process the event asynchronously.
- After a failure, Alt retries with progressively longer delays (30s โ 5m โ 30m โ 2h โ 12h โ 12h, up to 7 attempts total).
- An endpoint failing many consecutive deliveries is automatically disabled.
- Redirects are not followed; the webhook URL must answer directly over HTTPS.
Handle duplicate events
Webhook delivery is at-least-once, so retries or console replays can deliver the same event more than once. Keep a short-lived record of processed event_ids and skip events you have already handled.
Handle out-of-order events
Retries and parallel delivery can cause events to arrive in a different order from when they occurred. Do not assume the most recently received event contains the newest state. The server increases the note's revision whenever an API-visible field changes. Store the last revision applied for each note and ignore events with an equal or lower revision. If the correct order is unclear, fetch the note again from the REST API, which always returns the current revision.
Check for missed changes
- You can miss webhooks if your receiver stays unavailable beyond the retry window or the endpoint is disabled. Periodically call
GET /v1/notes?updated_after=<last sync>to fetch changes since your last sync. note.deletedwithreason: "access_lost"means the note left your credential's scope (e.g. a personal note moved into a teamspace). Treat it exactly like a deletion: remove or block access to your stored copy.- Incremental polling alone cannot detect deletions or scope loss. Periodically fetch the full note list as well. Use
include_deleted=trueto include deletion markers (tombstones). A stored note ID missing from the full list was deleted or left your scope.