# Receive lifecycle webhooks

Verify signed connection lifecycle events and process retries idempotently.

Authlane delivers connection changes through a transactional outbox with exponential retry.

## Prerequisites

Configure a tenant webhook URL and signing secret, preserve the raw request bytes, and keep a store
of processed event IDs.

## Implement the workflow

Verify the exact string `<timestamp>.<raw-body>` before parsing JSON:

```typescript
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyAuthlaneWebhook(
  rawBody: string,
  timestamp: string,
  signature: string,
  secret: string,
): boolean {
  const expected = Buffer.from(
    createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex'),
    'hex',
  );
  const received = Buffer.from(signature, 'hex');
  return received.length === expected.length && timingSafeEqual(received, expected);
}
```

Read `X-Authlane-Signature`, `X-Authlane-Timestamp`, `X-Authlane-Event`, and `Idempotency-Key`.
Reject timestamps outside a short tolerance such as five minutes, reserve the idempotency key
before side effects, and return `2xx` quickly.

Events are `connection.connected`, `connection.disconnected`, `connection.expired`,
`connection.refreshed`, and `connection.error`. Each payload includes `externalUserId`, `serviceId`,
and `connectionId` inside `data`.

## Expected result

Retries with the same event ID do not repeat side effects, and only a valid current signature
reaches business logic.

## Handle errors

Return non-`2xx` only when a retry can help. Monitor delivery backlog and clock drift; never log the
signing secret or raw sensitive payload extensions.

## Security boundary

Verify raw bytes and timestamp before parsing. Rotate the webhook secret through tenant settings and
retain the previous value only for the intended overlap.

## Next step

Use [connection lifecycle](/docs/guides/connection-lifecycle) to map events to UI state.
