# Receive webhooks Webhooks notify your backend of record changes, sync failures and runs needing input. ## Configure a destination On the Browserlayer worker, configure `BROWSERLAYER_WEBHOOK_TARGETS`: ```json { "development": { "url": "https://your-app.example/webhooks/browserlayer", "secret": "REPLACE_WITH_A_RANDOM_SECRET_OF_AT_LEAST_32_BYTES" } } ``` This is installation configuration, currently set by the installation operator. For the local CLI service, use the key `local`, set the variable before `browserlayer start`, and use a loopback receiver URL. Restart the service after changing targets. ## Verify the raw body Node backend example: ```ts import { verifyWebhook } from "@browserlayer/sync"; const raw = await request.text(); const valid = verifyWebhook( process.env.BROWSERLAYER_WEBHOOK_SECRET!, request.headers.get("x-browserlayer-timestamp") ?? "", raw, request.headers.get("x-browserlayer-signature") ?? "", ); if (!valid) return new Response("Invalid signature", { status: 401 }); const event = JSON.parse(raw); ``` `request` is the incoming Web Request in your route handler. Verify before parsing or trusting any event fields. ## Handle each event | `type` | Action | | -------------------- | ---------------------------------------------------------------------- | | `sync.updated` | Fetch and apply changes from your saved cursor | | `sync.failed` | Record the failure; clear it after a newer successful update | | `run.requires_input` | Show a way to [open hosted Link](/docs/connect#resume-an-existing-run) | All events include `id`, `createdAt`, `environment`, `integration` and `connection`. Sync events identify the sync; attention events identify the run and input purpose. Delivery is at least once. Deduplicate event IDs and tolerate out-of-order delivery. Check current run status before showing an old attention event. Return success only after durable processing or durable queue admission. Failed delivery is retried with backoff, up to 12 attempts. Operators can inspect `/deliveries` and retry `/deliveries/:id/retry`. Your consumer must also be able to catch up from its saved cursor after downtime.