> ## Documentation Index
> Fetch the complete documentation index at: https://the-early-spring18.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks API: Register Endpoints for Real-Time Events

> Register and manage webhook endpoints to receive real-time HTTP POST notifications when data changes or user events occur in your account.

Webhooks allow your application to react to events in your Google account the moment they happen, without the need for repeated polling. When a relevant event occurs — such as a new data object being created or a user being invited — Google sends an HTTP `POST` request to a URL of your choosing, containing a JSON payload with full details about the event. You can register multiple webhook endpoints, each subscribed to a different set of events, giving you fine-grained control over which parts of your system are notified about which activity.

## Supported Events

The following events can be subscribed to when you register a webhook endpoint. Each event name corresponds to a specific action taken within your account.

| Event          | Description                                                                         |
| -------------- | ----------------------------------------------------------------------------------- |
| `data.created` | Fired when a new data object is successfully created in your account.               |
| `data.updated` | Fired when an existing data object's title, content, tags, or metadata is modified. |
| `data.deleted` | Fired when a data object is permanently deleted from your account.                  |
| `user.created` | Fired when a new user is invited to your account via `POST /v1/users`.              |

## Payload Structure

All events share a common JSON envelope, with a top-level `event` field identifying the event type and a `data` field containing the relevant object. Your endpoint will always receive a `Content-Type: application/json` header alongside the payload body.

```json theme={null}
{
  "event": "data.created",
  "timestamp": "2024-06-01T10:00:00Z",
  "account_id": "acc_xyz123",
  "data": {
    "id": "dat_abc123",
    "title": "New Report",
    "type": "document"
  }
}
```

***

## POST /v1/webhooks

Register a new webhook endpoint that will receive event notifications. You can subscribe each endpoint to one or more of the supported event types. Optionally, you can provide a secret that Google will use to sign every payload, allowing your server to verify the request is authentic. This endpoint requires **write** scope and returns `201 Created` on success.

### Request Body

<ParamField body="url" type="string" required>
  The publicly accessible HTTPS URL to which Google will send event payloads. The URL must respond with a `2xx` status code within 10 seconds to be considered a successful delivery. HTTP URLs are not accepted — your endpoint must use HTTPS.
</ParamField>

<ParamField body="events" type="array" required>
  An array of event name strings to subscribe this endpoint to. At least one event name is required. Valid values are `data.created`, `data.updated`, `data.deleted`, and `user.created`. Pass `["*"]` to subscribe to all current and future events.
</ParamField>

<ParamField body="secret" type="string">
  An optional secret string used to generate an HMAC-SHA256 signature for each outgoing payload. When provided, Google includes a `X-Google-Signature` header on every request so your server can verify the payload's authenticity. See [Verifying Webhook Signatures](#verifying-webhook-signatures) below.
</ParamField>

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.google.com/v1/webhooks \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "url": "https://yourapp.com/webhooks",
      "events": ["data.created", "data.updated"],
      "secret": "your_signing_secret"
    }'
  ```
</CodeGroup>

### Example Response

```json theme={null}
{
  "data": {
    "id": "wh_abc123",
    "url": "https://yourapp.com/webhooks",
    "events": ["data.created", "data.updated"],
    "status": "active",
    "created_at": "2024-06-01T10:00:00Z"
  }
}
```

### Response Fields

<ResponseField name="id" type="string">
  The unique identifier for the newly registered webhook, prefixed with `wh_`.
</ResponseField>

<ResponseField name="url" type="string">
  The HTTPS URL that will receive event payloads.
</ResponseField>

<ResponseField name="events" type="array">
  The list of event names this endpoint is subscribed to, as provided in the request body.
</ResponseField>

<ResponseField name="status" type="string">
  The current status of the webhook. Newly registered webhooks start as `active`.
</ResponseField>

<ResponseField name="created_at" type="string">
  The ISO 8601 timestamp at which the webhook was registered, in UTC.
</ResponseField>

***

## GET /v1/webhooks

Retrieve a list of all webhook endpoints currently registered to your account, including their subscribed events and status.

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.google.com/v1/webhooks \
    -H 'Authorization: Bearer YOUR_API_KEY'
  ```
</CodeGroup>

### Example Response

```json theme={null}
{
  "data": {
    "webhooks": [
      {
        "id": "wh_abc123",
        "url": "https://yourapp.com/webhooks",
        "events": ["data.created", "data.updated"],
        "status": "active",
        "created_at": "2024-06-01T10:00:00Z"
      }
    ],
    "total": 1
  }
}
```

### Response Fields

<ResponseField name="webhooks" type="array">
  An array of registered webhook endpoint objects.

  <Expandable title="webhooks[]">
    <ResponseField name="id" type="string">
      The unique identifier for the webhook, prefixed with `wh_`.
    </ResponseField>

    <ResponseField name="url" type="string">
      The HTTPS URL that receives event payloads.
    </ResponseField>

    <ResponseField name="events" type="array">
      The list of event names this endpoint is subscribed to.
    </ResponseField>

    <ResponseField name="status" type="string">
      The current status of the webhook. Either `active` or `disabled`. An endpoint may be automatically disabled after repeated delivery failures.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      The ISO 8601 timestamp at which the webhook was registered, in UTC.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total" type="integer">
  The total number of webhook endpoints registered to your account.
</ResponseField>

***

## DELETE /v1/webhooks/{id}

Remove a registered webhook endpoint from your account. Once deleted, no further event payloads will be sent to that URL. This endpoint returns `204 No Content` on success with an empty response body.

### Path Parameters

<ParamField path="id" type="string" required>
  The unique identifier of the webhook to remove, for example `wh_abc123`. You can retrieve this from the `GET /v1/webhooks` listing.
</ParamField>

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://api.google.com/v1/webhooks/wh_abc123 \
    -H 'Authorization: Bearer YOUR_API_KEY'
  ```
</CodeGroup>

A successful response returns HTTP `204 No Content` with no body.

### Response Fields

This endpoint returns no response body. A `204 No Content` status code confirms the webhook endpoint was successfully removed.

***

## Verifying Webhook Signatures

If you provided a `secret` when registering your webhook, Google signs the raw JSON payload body using **HMAC-SHA256** and includes the resulting hex digest in the `X-Google-Signature` request header. You should always verify this signature before processing the event to confirm the request genuinely originated from Google and was not tampered with in transit.

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(secret: str, payload_bytes: bytes, received_sig: str) -> bool:
      expected_sig = hmac.new(
          secret.encode(),
          payload_bytes,
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(expected_sig, received_sig)

  # Usage in your request handler:
  # verify_signature(SECRET, request.body, request.headers['X-Google-Signature'])
  ```

  ```javascript JavaScript theme={null}
  const crypto = require('crypto');

  function verifySignature(secret, body, receivedSig) {
    const expectedSig = crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');
    return crypto.timingSafeEqual(
      Buffer.from(expectedSig),
      Buffer.from(receivedSig)
    );
  }

  // Usage in your request handler:
  // verifySignature(SECRET, req.rawBody, req.headers['x-google-signature']);
  ```
</CodeGroup>

<Info>
  Always compare signatures using a **timing-safe equality function** (such as `hmac.compare_digest` in Python or `crypto.timingSafeEqual` in Node.js) to prevent timing-based attacks.
</Info>

<Tip>
  Respond to incoming webhook requests with an HTTP `200` status code as quickly as possible — ideally within 2–3 seconds — and handle any heavy processing asynchronously in a background job or queue. If your endpoint takes too long to respond, the delivery will be treated as a failure and retried, potentially causing duplicate processing on your side.
</Tip>

<Note>
  If your endpoint does not return a `2xx` response within 10 seconds, Google treats the delivery as failed and retries it up to **5 times** using exponential backoff. Retry intervals are approximately 1 min, 5 min, 30 min, 2 hours, and 8 hours. After 5 failed attempts, no further retries are made and the webhook endpoint may be automatically disabled.
</Note>
