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

# Webhooks

## Overview

Webhooks provide a way for your application to receive real-time notifications about specific events. By configuring webhooks, you can automate workflows and seamlessly integrate our platform with your application.

## Configuration

To set up a webhook, follow these steps:

1. **Navigate to Settings:** Go to the [Integrations Page](https://app.meetgeek.ai/integrations) and open the **Public API** section;
2. **Enter Webhook URL:** Specify the endpoint where webhook events should be sent;
3. **Save Changes:** Click **Save** to activate the webhook.

## Event Notifications

When a meeting analysis is completed, the system sends three `POST` requests to the configured webhook URL. The request payloads are in JSON format, as shown below:

#### Successful Analysis:

```json theme={null}
{
    "message": "File analyzed successfully",
    "meeting_id": "9433976b-cfa3-4373-9afb-eb0b7a5915df"
}
```

#### Failed Analysis:

```json theme={null}
{
    "message": "File analyzed failed"
}
```

Your server must respond with an `HTTP 200 OK` status code and no response body to acknowledge receipt of the webhook. The second and third POST requests are sent only as retries in case the initial request is not acknowledged. If your server successfully responds with 200 OK, no further retries will be made.

## Team-Level Webhooks

For meetings that are shared with a team, webhooks are triggered for all team members who have view access to the meeting. This ensures that relevant users receive timely notifications about the meeting’s analysis status.

<Note>The meeting owner must have Share Access to the teams they wish to share with. If they do not have Share Access, the webhook notification will be sent only to them.</Note>

To manage access permissions, go to the [Team Sharing](https://app.meetgeek.ai/teams) page and update the sharing settings for each team member.

## Webhook Security

To ensure the authenticity and integrity of webhook payloads, MeetGeek adds a new header:

```http theme={null}
X-MG-Signature: <hex_hmac_of_body>
```

### How it works

* MeetGeek computes an HMAC SHA-256 of the exact raw request body (`body_bytes`), using the configured secret;
* Clients should recompute the HMAC and compare it against the header using a constant-time comparison function to avoid timing attacks.

### Verification Examples

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

  def is_valid(secret: str, body: bytes, sig_header: str) -> bool:
      expected = hmac.new(
          key=secret.encode(),
          msg=body,
          digestmod=hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(f"{expected}", sig_header)
  ```

  ```go Go theme={null}
  import (
      "crypto/hmac"
      "crypto/sha256"
      "encoding/hex"
  )

  func IsValid(secret []byte, body []byte, sigHeader string) bool {
      mac := hmac.New(sha256.New, secret)
      mac.Write(body)
      expected := hex.EncodeToString(mac.Sum(nil))
      return hmac.Equal([]byte(expected), []byte(sigHeader))
  }
  ```

  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  // body must be raw buffer from req.body
  function isValid(secret, body, sigHeader) {
      const expected = crypto
          .createHmac('sha256', secret)
          .update(body)
          .digest('hex');
      
      return crypto.timingSafeEqual(
          Buffer.from(expected),
          Buffer.from(sigHeader)
      );
  }
  ```

  ```java Java (Spring Boot) theme={null}
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import java.security.MessageDigest;

  public static boolean isValidSignature(String secret, byte[] body, String headerSig) throws Exception {
      Mac hmac = Mac.getInstance("HmacSHA256");
      hmac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
      byte[] hash = hmac.doFinal(body);
      String expected = bytesToHex(hash);
      return MessageDigest.isEqual(expected.getBytes("UTF-8"), headerSig.getBytes("UTF-8"));
  }

  private static String bytesToHex(byte[] bytes) {
      StringBuilder sb = new StringBuilder();
      for (byte b : bytes) sb.append(String.format("%02x", b));
      return sb.toString();
  }
  ```
</CodeGroup>

### Best Practices

* Always use **constant-time comparison** (`compare_digest`, `hmac.Equal`, `MessageDigest.isEqual`) to prevent timing attacks;
* Compute the HMAC over the **exact raw request body** — please avoid re-serializing or formatting it;
* If validation fails, return **HTTP 401 Unauthorized** and do not process the request.
