Build

Outbound webhooks

Receive selected Lyra events over durable, signed HTTPS deliveries without maintaining a Gateway connection.

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.

json
{
  "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.

javascript
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.

StatusMeaning
pendingPersisted and waiting for a worker
processingLeased by one worker
retryingA retryable attempt failed
succeededThe endpoint returned 2xx
failedPermanent response or retry budget exhausted

Management endpoints

MethodPathPurpose
GET/applications/:app_id/webhooksList endpoints and supported events
POST/applications/:app_id/webhooksCreate an endpoint; returns the secret once
PATCH/applications/:app_id/webhooks/:webhook_idUpdate URL, events, name or active state
DELETE/applications/:app_id/webhooks/:webhook_idDelete endpoint and its delivery history
POST/applications/:app_id/webhooks/:webhook_id/regenerate-secretRotate and reveal a new secret once
POST/applications/:app_id/webhooks/:webhook_id/testQueue a WEBHOOK_TEST delivery
GET/applications/:app_id/webhook-deliveriesList retained delivery results