Skip to main content
← API Reference
Webhooks

Webhooks

Lazynext sends signed POSTs to your endpoint when events fire. Each delivery is signed, retried on transient failure, and includes the headers you need to verify it.

Headers

POST /your-endpoint HTTP/1.1
Content-Type: application/json
X-Lazynext-Event: decision.created
X-Lazynext-Delivery: <uuid>
X-Lazynext-Timestamp: 1745842800
X-Lazynext-Signature: sha256=...

Verifying a delivery (Node.js)

Compute HMAC-SHA256 over <timestamp>.<raw-body> using your endpoint's shared secret. Compare with crypto.timingSafeEqual to avoid timing attacks. Reject deliveries older than 5 minutes (replay protection).

import crypto from 'node:crypto'

export function verifyLazynextWebhook(
  rawBody: string,
  headers: { signature: string; timestamp: string },
  secret: string,
): boolean {
  // 1. Replay protection — reject deliveries older than 5 minutes.
  const ts = Number(headers.timestamp)
  if (!Number.isFinite(ts)) return false
  if (Math.abs(Date.now() / 1000 - ts) > 300) return false

  // 2. Strip the "sha256=" prefix.
  const expected = headers.signature.replace(/^sha256=/, '')

  // 3. Compute the signature over <timestamp>.<rawBody>.
  const actual = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex')

  // 4. Constant-time compare.
  const a = Buffer.from(actual, 'hex')
  const b = Buffer.from(expected, 'hex')
  if (a.length !== b.length) return false
  return crypto.timingSafeEqual(a, b)
}

Use the raw request body, not a JSON-parsed copy — re-serializing reorders keys and breaks the signature.

Retry policy

On 2xx we mark the delivery successful. On 4xx (other than 408 / 429) we stop retrying — your endpoint told us the payload is bad. On 5xx, 408, 429, or a network timeout we retry with exponential backoff:

  • 30 seconds
  • 2 minutes
  • 10 minutes
  • 1 hour
  • 6 hours

After 5 attempts the delivery is marked failed. You can replay any failed delivery from Settings → Webhooks.

Idempotency

Use X-Lazynext-Delivery as your dedup key. We may deliver the same delivery id twice in rare cases (network mid-flight, aggressive retry). Your handler must be idempotent — store the delivery id and skip if you've seen it.

Events shipping in v1

  • decision.created
  • decision.outcome_logged
  • workspace.member_added
  • workspace.member_removed
  • billing.plan_changed