> ## Documentation Index
> Fetch the complete documentation index at: https://docs.browserbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Verifying deliveries

> Confirm a request came from Browserbase before acting on it.

Your endpoint is a public URL, so anyone can POST to it. Verify the signature on every request and reject anything that fails.

## Verify the signature

Each delivery carries `webhook-id`, `webhook-timestamp`, and `webhook-signature`. Verification is an HMAC over those values and the raw body, using the signing secret Browserbase returned when you created or rotated the webhook.

Deliveries follow the [Standard Webhooks](https://www.standardwebhooks.com) specification, so any library implementing it will verify them. Use one instead of writing the HMAC yourself. It handles the constant-time comparison and the timestamp tolerance for you.

The examples below use Express and FastAPI.

<CodeGroup>
  ```typescript Node.js theme={null}
  import { Webhook } from "standardwebhooks";
  import express from "express";

  const app = express();
  const wh = new Webhook(process.env.BROWSERBASE_WEBHOOK_SECRET!);

  // Use the raw body. Any reserialization changes the bytes and breaks the
  // signature.
  app.post(
    "/browserbase/events",
    express.raw({ type: "application/json" }),
    (req, res) => {
      let event;
      try {
        event = wh.verify(req.body, {
          "webhook-id": req.header("webhook-id")!,
          "webhook-timestamp": req.header("webhook-timestamp")!,
          "webhook-signature": req.header("webhook-signature")!,
        });
      } catch {
        return res.sendStatus(400);
      }

      // Acknowledge first, then do the work.
      res.sendStatus(204);
      void handleEvent(event);
    },
  );
  ```

  ```python Python theme={null}
  import os
  from fastapi import FastAPI, Request, Response
  from standardwebhooks.webhooks import Webhook, WebhookVerificationError

  app = FastAPI()
  wh = Webhook(os.environ["BROWSERBASE_WEBHOOK_SECRET"])


  @app.post("/browserbase/events")
  async def receive(request: Request) -> Response:
      # Use the raw body. Any reserialization changes the bytes and breaks the
      # signature.
      body = await request.body()
      try:
          event = wh.verify(body, dict(request.headers))
      except WebhookVerificationError:
          return Response(status_code=400)

      handle_event(event)
      return Response(status_code=204)
  ```
</CodeGroup>

<Warning>
  Verify against the **raw request body**. Frameworks that parse JSON and hand you an object have already changed the bytes, so the signature will not match.
</Warning>

## Handle redelivery

Browserbase retries a delivery when your endpoint doesn't return `2xx` within 15 seconds, so the same event can arrive more than once. That includes arriving after you processed it but before your response landed.

Treat handlers as idempotent. The envelope `id` is stable across retries of the same event, so it works as a deduplication key:

```typescript Node.js theme={null}
if (await alreadyProcessed(event.id)) {
  return; // already handled, nothing to do
}
await process(event);
await markProcessed(event.id);
```

Browserbase doesn't order events for one resource. A `completed` event can arrive before the `running` event for the same invocation, so branch on `type` and the state in `data` instead of assuming arrival order.

## When deliveries stop

Browserbase retries failures immediately, then after 5s, 5m, 30m, 2h, 5h, 10h, and 10h. After eight attempts Browserbase marks the message failed and stops retrying.

Browserbase disables an endpoint that fails continuously for five days. If deliveries stop without an obvious cause, check that before anything else.

These common failure modes are preventable:

* **Slow handlers.** The 15-second budget covers your whole response. Acknowledge first, process after.
* **Redirects.** A `3xx` is a failure, not a success. Register the final URL instead of one that redirects to it.

<CardGroup cols={2}>
  <Card title="Webhooks API reference" icon="code" href="/reference/api/create-a-webhook">
    Full request and response shapes for every endpoint.
  </Card>

  <Card title="Functions" icon="code-simple" href="/platform/runtime/overview">
    The invocations and builds these events report on.
  </Card>
</CardGroup>
