# Guide to MeetGeek Webhooks: Features and Best Practices

MeetGeek is an AI meeting assistant: it joins or records your Zoom, Google Meet and Teams calls, then produces a transcript, summary, highlights and action items. Analysis takes a while after the meeting ends, and MeetGeek's webhook is the signal that it's done, so your CRM, project tracker or knowledge base can pull the results through MeetGeek's Public API without polling.

This guide covers the single notification MeetGeek sends, how it's tied to individual users, verifying `X-MG-Signature`, and the gaps you'll need to work around.

## What are MeetGeek webhooks?

MeetGeek has one webhook, and it's per user: each person sets a single URL in the **Public API** section of their **Integrations** page. When analysis of one of their meetings finishes, MeetGeek sends a JSON `POST` to that URL.

The body is minimal. A successful analysis sends a `message` and the `meeting_id`; a failed one sends only a `message`. Everything else (transcript, summary, highlights) comes from the Public API, using the `meeting_id`.

Requests carry `X-MG-Signature`, a hex HMAC-SHA256 of the raw body, keyed with the secret configured for the Public API.

## MeetGeek webhook features

| Feature | Details |
| --- | --- |
| Configuration | **Integrations > Public API**, one URL per user |
| Trigger | Meeting analysis finished (success or failure) |
| Signature | hex HMAC-SHA256 |
| Signature header | `x-mg-signature` |
| Signed content | The raw request body |
| Key | "The configured secret" for the Public API (location undocumented) |
| Body | JSON: `message`, plus `meeting_id` on success |
| Event routing | None; branch on `meeting_id` |
| Acknowledgement | `200 OK`, no body needed |
| Retries | Up to 2 (3 POSTs in total) |

## Events and payloads

| Event | Body | Use cases |
| --- | --- | --- |
| `successful-analysis` | `{"message": "File analyzed successfully", "meeting_id": "9433976b-cfa3-4373-9afb-eb0b7a5915df"}` | Fetch transcript, summary and highlights via the Public API |
| `failed-analysis` | `{"message": "File analyzed failed"}` | Alert, or retry the upload |

`successful-analysis` and `failed-analysis` are the docs' section headings, used as sample file names; they aren't values sent on the wire. The `message` strings are human-readable text, so test for `meeting_id` rather than matching the wording.

### Published payload example

The failure form, from the [webhook samples library](https://github.com/hookdeck/webhook-samples/blob/main/providers/meetgeek/latest/failed-analysis.json), shows how little it carries: no meeting ID, user or time.

```json
{
  "message": "File analyzed failed"
}

```

## Setting up MeetGeek webhooks

### Prerequisites

* A MeetGeek account with access to the Public API
* A public HTTPS endpoint that accepts `POST`

### Register Your Endpoint

1. Open the **Integrations** page in MeetGeek and go to the **Public API** section.
2. Enter your webhook URL and save. Each user has one URL.
3. Have each team member whose meetings you need do the same, or rely on team sharing (see below).

### Get Your Webhook Secret

Paste the webhook secret configured for the MeetGeek Public API. Deliveries without a signature fail verification.

MeetGeek's docs call the key "the configured secret" without saying where it's set or shown; look in the same Public API section as the webhook URL. Store it as `WEBHOOK_SECRET` and use it as UTF-8 bytes, unmodified.

### Test a delivery

Start a local receiver and forward requests with the [Hookdeck CLI](/docs/cli):

```bash
npx hookdeck-cli listen 3000 meetgeek --path /webhooks/meetgeek

```

Set the CLI's URL as your webhook, record a short test meeting, and wait for analysis to finish. Then call the Public API with the `meeting_id` you receive.

## Securing MeetGeek webhooks

Compute HMAC-SHA256 over the raw body with the secret's UTF-8 bytes, hex-encode it, and compare it with `X-MG-Signature` in constant time. MeetGeek's [webhook appendix](https://docs.meetgeek.ai/api/api-reference/v1/appendix-webhooks) has samples in Python, Go, Node and Java.

```javascript
const crypto = require('node:crypto');

function verifySignature(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
  const signature = headers['x-mg-signature'];
  if (typeof signature !== 'string') return false;
  const key = Buffer.from(secret, 'utf8');
  const message = rawBody;
  const candidates = [signature];
  const expected = crypto.createHmac('sha256', key).update(message).digest();
  return candidates.some((candidate) => {
    if (!/^[0-9a-fA-F]{64}$/.test(candidate)) return false;
    const received = Buffer.from(candidate, 'hex');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

```

The handler acknowledges with a plain `200`, ignores a `meeting_id` it has already queued, and flags failures for follow-up.

```javascript
const express = require('express');

function createMeetGeekApp({ queueMeetingFetch, alreadyQueued, reportFailure }) {
  const app = express();
  app.post('/webhooks/meetgeek', express.raw({ type: '*/*' }), async (req, res) => {
    const secret = process.env.WEBHOOK_SECRET;
    if (!secret) return res.sendStatus(500);
    if (!verifySignature(req.body, req.headers, secret)) return res.sendStatus(401);
    let parsed;
    try {
      parsed = JSON.parse(req.body.toString('utf8'));
    } catch {
      return res.sendStatus(400);
    }
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return res.sendStatus(400);
    const { meeting_id, message } = parsed;
    if (!meeting_id && !message) return res.sendStatus(400);
    try {
      if (meeting_id) {
        if (!(await alreadyQueued(meeting_id))) await queueMeetingFetch(meeting_id);
      } else {
        await reportFailure(message);
      }
    } catch {
      return res.sendStatus(503);
    }
    return res.sendStatus(200);
  });
  return app;
}

```

The signature covers only the body, and the success body for a given meeting never changes, so a replay verifies indefinitely. Deduplicating on `meeting_id` is enough to make that harmless.

Hookdeck's MeetGeek source type verifies `X-MG-Signature` over the raw body. MeetGeek gets `200` every time, which also stops it retrying; a failed check is recorded as failed verification and the request isn't passed on. There's no timestamp, and so no tolerance to apply.

> **Make MeetGeek webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) collects every user's MeetGeek webhooks on one verified source, removes duplicate `meeting_id` deliveries, and keeps them replayable beyond MeetGeek's two retries.

## MeetGeek webhook limitations and pain points

### Failures don't say which meeting

**The Problem:** You know an analysis failed, but not which meeting or whose.

**Why It Happens:** The failure body is only `{"message": "File analyzed failed"}`.

**Workarounds:**

* Track meetings you expect to be analysed and look for ones that never produce a success webhook.
* Use separate URLs per user (for example a path segment) so at least the user is known.

**How Hookdeck Can Help:** Hookdeck's request log records when each failure arrived and on which source, which helps you match it to a recording.

### One URL per user

**The Problem:** Collecting every meeting in an organisation means configuring the webhook for each user.

**Why It Happens:** The webhook is set on each user's Integrations page, with no organisation-level setting documented.

**Workarounds:**

* Have each user point at the same receiver, with a user identifier in the path.
* Use team sharing so meetings reach a user who has the webhook set.

**How Hookdeck Can Help:** One Hookdeck source URL can be given to every user, so all deliveries land in one place with one secret check.

### Duplicates from team sharing and retries

**The Problem:** The same meeting is processed several times.

**Why It Happens:** For a team-shared meeting, every member with view access gets the webhook, and unacknowledged requests are retried up to twice.

**Workarounds:**

* Deduplicate on `meeting_id` before fetching from the API.

**How Hookdeck Can Help:** Hookdeck deduplication on `meeting_id` drops repeats before they reach your service.

### Short, unspecified retry window

**The Problem:** A brief outage can lose the notification for good.

**Why It Happens:** MeetGeek sends at most three requests and documents neither the spacing nor the timeout.

**Workarounds:**

* Periodically list recent meetings through the Public API to catch any you missed.

**How Hookdeck Can Help:** Hookdeck accepts the first request and holds it until your service is ready, retrying on a schedule you set.

### Undocumented secret location

**The Problem:** It's unclear where the signing key comes from or how to rotate it.

**Why It Happens:** The docs refer to "the configured secret" only.

**Workarounds:**

* Check the Public API section of the Integrations page, and ask MeetGeek support if it isn't there.
* Test with a real delivery before enforcing verification in production.

**How Hookdeck Can Help:** Failed verifications are visible in Hookdeck, so a wrong or rotated secret shows up straight away.

## Best practices

### Answer with a bare 200

MeetGeek retries anything that isn't acknowledged with `200 OK`; no body is needed. Return it as soon as the notification is stored.

### Fetch results in the background

The webhook only says analysis is ready. Calling the Public API for the transcript and summary takes time, so do it in a worker, as described in our [asynchronous processing guide](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Key everything on `meeting_id`

It's the only identifier in the payload. Store it with whatever you create from the meeting, following the [idempotency guide](/webhooks/guides/implement-webhook-idempotency).

### Protect meeting content

Transcripts and summaries fetched after the webhook can contain sensitive discussion. Apply the same access controls and retention to them as to the original recordings.

## Conclusion

MeetGeek's webhook does one job, telling you a meeting's analysis is ready, and the HMAC signature on it is easy to check. Its limits are the per-user setup, a failure body with no meeting ID, duplicate deliveries for shared meetings and only two retries, so pair it with `meeting_id` deduplication and a periodic API sweep.

[Hookdeck Event Gateway](https://hookdeck.com) brings every user's MeetGeek notifications into one verified, deduplicated and replayable stream.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle MeetGeek webhooks reliably in minutes.