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

# Webhooks

> Receive signed notifications for agent actions and owner policy changes.

Webhooks tell your server when an action changes state or an owner policy is confirmed or revoked. Delivery is at least once and may be out of order. Deduplicate by event ID and treat [polling](/guides/hosted-gas#7-observe-the-result) as the source of truth.

## Create an endpoint

In the console, open **Webhooks** and choose **Add endpoint**, or call the API:

```ts theme={null}
const { endpoint, secret } = await gol.createWebhookEndpoint(projectId, {
  url: "https://example.com/gol/webhooks",
  eventTypes: ["gas_execution.updated", "gas_policy.confirmed", "gas_policy.revoked"],
});
// Store `secret` (whsec_...). It is shown once.
```

The URL must be `https` and resolve to a public address. Redirects are not followed. A project environment can have up to 10 endpoints. Send a test event with **Test** in the console or `sendWebhookTest`.

## Events

| Type                    | `data`                                            |
| ----------------------- | ------------------------------------------------- |
| `gas_execution.updated` | The full action, as returned by `getGasExecution` |
| `gas_policy.confirmed`  | The owner gas policy                              |
| `gas_policy.revoked`    | The owner gas policy                              |
| `webhook.test`          | `{ endpointId }`                                  |

```json theme={null}
{
  "id": "evt_4c2f...",
  "type": "gas_execution.updated",
  "createdAt": "2026-09-24T15:40:12.000Z",
  "projectId": "5d7e2b3c-...",
  "environment": "test",
  "data": { "id": "...", "state": "collected", "receipt": { "...": "..." } }
}
```

## Verify every delivery

Each request carries `gol-webhook-id`, `gol-webhook-timestamp`, and `gol-webhook-signature`. The signature is `v1=` followed by the hex HMAC-SHA256 of `timestamp.rawBody`, keyed by the endpoint secret. Verify against the raw body before parsing:

```ts theme={null}
import express from "express";
import { verifyWebhook } from "@gol/sdk/server";

app.post("/gol/webhooks", express.raw({ type: "application/json" }), (req, res) => {
  try {
    const event = verifyWebhook(req.body, req.headers, process.env.GOL_WEBHOOK_SECRET!);
    // handle event.type, deduplicating by event.id
    res.sendStatus(204);
  } catch {
    res.sendStatus(400);
  }
});
```

Timestamps more than 5 minutes from your clock are rejected. When you rotate a secret, both secrets sign deliveries for 24 hours; pass both to `verifyWebhook` during the switch.

## Retries

Respond with any `2xx` within 10 seconds. Otherwise GOL retries after about 30 seconds, 2, 10, and 30 minutes, 1, 3, 6, and 12 hours, then gives up. Every attempt appears under **Recent deliveries** in the console and in `listWebhookDeliveries`. A disabled endpoint keeps pending deliveries for 72 hours.
