Configure an endpoint
Open an application in the developer console, select Webhooks, then register a public HTTPS URL and the event families you need. An application can own up to 10 endpoints. Events are sent only for communities where the application bot is installed. Private, loopback and link-local destinations are rejected. Redirects are never followed.
The signing secret is shown once
Store it in a secrets manager immediately. Lyra encrypts it at rest; rotating it invalidates the old secret for every delivery that has not yet been attempted.
Delivery envelope
Every callback is an HTTP POST with application/json. id is stable across retries, event names the subscribed event and data contains the original Gateway dispatch payload. Return any 2xx status within 10 seconds to acknowledge it.
{
"id": "<delivery_id>",
"event": "MESSAGE_CREATE",
"created_at": "2026-08-30T12:00:00Z",
"data": { "id": "<message_id>", "content": "Hello" }
}Verify the signature
Lyra sends X-Lyra-Event, X-Lyra-Delivery, X-Lyra-Timestamp and X-Lyra-Signature-256. Compute HMAC-SHA256 with the signing secret over the UTF-8 bytes of “timestamp.raw_request_body”, prefix the lowercase hexadecimal digest with v1=, then compare in constant time. Reject stale timestamps before parsing or acting on the payload.
import crypto from 'node:crypto'
export function verifyLyraWebhook(rawBody, headers, secret) {
const timestamp = headers['x-lyra-timestamp']
const received = headers['x-lyra-signature-256']
if (!timestamp || !received) return false
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false
const expected = 'v1=' + crypto
.createHmac('sha256', secret)
.update(timestamp + '.')
.update(rawBody)
.digest('hex')
const left = Buffer.from(expected)
const right = Buffer.from(received)
return left.length === right.length && crypto.timingSafeEqual(left, right)
}Retries and delivery history
Network failures, HTTP 408, 425, 429 and 5xx responses are retried with exponential backoff for up to eight attempts. Other 4xx responses fail immediately. Delivery IDs make handlers idempotent: persist each processed ID before applying side effects. The console exposes status, attempt count, response code, latency and the last error for 30 days.
| Status | Meaning |
|---|---|
| pending | Persisted and waiting for a worker |
| processing | Leased by one worker |
| retrying | A retryable attempt failed |
| succeeded | The endpoint returned 2xx |
| failed | Permanent response or retry budget exhausted |
Management endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /applications/:app_id/webhooks | List endpoints and supported events |
| POST | /applications/:app_id/webhooks | Create an endpoint; returns the secret once |
| PATCH | /applications/:app_id/webhooks/:webhook_id | Update URL, events, name or active state |
| DELETE | /applications/:app_id/webhooks/:webhook_id | Delete endpoint and its delivery history |
| POST | /applications/:app_id/webhooks/:webhook_id/regenerate-secret | Rotate and reveal a new secret once |
| POST | /applications/:app_id/webhooks/:webhook_id/test | Queue a WEBHOOK_TEST delivery |
| GET | /applications/:app_id/webhook-deliveries | List retained delivery results |