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

# Webhooks

> Receive real-time notifications when events happen in Mavio via HTTP webhooks.

Webhooks let your application receive real-time HTTP notifications when events happen in Mavio. Instead of polling the API for changes, register a webhook endpoint and Mavio pushes event data to you as it happens.

## Setting up a webhook

<Steps>
  <Step title="Open webhook settings">
    Navigate to **Settings > Developer > Webhooks** and click **Add Endpoint**.
  </Step>

  <Step title="Configure the endpoint">
    Provide:

    * **URL** — the HTTPS endpoint where Mavio sends events (must be publicly accessible)
    * **Events** — select which events trigger this webhook (see event types below)
    * **Description** — optional label for this endpoint
  </Step>

  <Step title="Receive the signing secret">
    Mavio generates a signing secret for this endpoint. Copy it and store it securely. You use this to verify that incoming requests are genuinely from Mavio.
  </Step>

  <Step title="Test the endpoint">
    Click **Send Test Event** to verify your endpoint receives and responds correctly. Mavio sends a `ping` event and expects a `200` response.
  </Step>
</Steps>

<Tip>
  Use a service like [webhook.site](https://webhook.site) during development to inspect incoming payloads before building your handler.
</Tip>

## Event types

| Event                | Trigger                       | Payload includes                                  |
| -------------------- | ----------------------------- | ------------------------------------------------- |
| `meeting.started`    | A meeting recording begins    | Meeting ID, title, participants, recording method |
| `meeting.completed`  | A meeting recording ends      | Meeting ID, title, duration, participant count    |
| `transcript.ready`   | Transcription is complete     | Meeting ID, transcript ID, language, word count   |
| `summary.ready`      | AI summary is generated       | Meeting ID, summary text, key topics              |
| `action_items.ready` | Action items are extracted    | Meeting ID, action items array with assignees     |
| `minutes.ready`      | Meeting minutes are generated | Meeting ID, minutes content                       |
| `diagram.ready`      | A diagram has been generated  | Meeting ID, diagram ID, diagram type              |
| `speaker.identified` | A new speaker is identified   | Meeting ID, speaker profile ID, confidence        |
| `meeting.shared`     | A meeting is shared           | Meeting ID, shared with user ID, permission level |
| `meeting.deleted`    | A meeting is deleted          | Meeting ID, deleted by user ID                    |

<AccordionGroup>
  <Accordion title="meeting.completed payload example">
    ```json theme={null}
    {
      "id": "evt_a1b2c3d4e5f6",
      "type": "meeting.completed",
      "created_at": "2026-04-14T15:30:00Z",
      "data": {
        "meeting_id": "mtg_abc123",
        "title": "Product Roadmap Review",
        "duration_seconds": 2700,
        "participant_count": 5,
        "recording_method": "meeting_bot",
        "workspace_id": "ws_xyz789"
      }
    }
    ```
  </Accordion>

  <Accordion title="transcript.ready payload example">
    ```json theme={null}
    {
      "id": "evt_f6e5d4c3b2a1",
      "type": "transcript.ready",
      "created_at": "2026-04-14T15:35:00Z",
      "data": {
        "meeting_id": "mtg_abc123",
        "transcript_id": "trs_def456",
        "language": "en",
        "word_count": 8420,
        "speaker_count": 5,
        "duration_seconds": 2700
      }
    }
    ```
  </Accordion>

  <Accordion title="action_items.ready payload example">
    ```json theme={null}
    {
      "id": "evt_x9y8z7w6v5u4",
      "type": "action_items.ready",
      "created_at": "2026-04-14T15:36:00Z",
      "data": {
        "meeting_id": "mtg_abc123",
        "action_items": [
          {
            "id": "ai_001",
            "text": "Send updated proposal to client",
            "assignee": "Alice Johnson",
            "due_date": "2026-04-18",
            "priority": "high"
          },
          {
            "id": "ai_002",
            "text": "Schedule follow-up meeting with engineering",
            "assignee": "Bob Smith",
            "due_date": null,
            "priority": "medium"
          }
        ]
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Payload format

All webhook payloads follow this structure:

```json theme={null}
{
  "id": "evt_unique_event_id",
  "type": "event.type",
  "created_at": "2026-04-14T10:30:00Z",
  "data": { }
}
```

### HTTP headers

Each webhook request includes the following headers:

```
Content-Type: application/json
X-Mavio-Signature: sha256=xxxxxxxxxxxxxxxx
X-Mavio-Event: meeting.completed
X-Mavio-Delivery: dlv_unique_id
X-Mavio-Timestamp: 1713105000
```

## Signature verification

Verify webhook signatures to ensure requests are genuinely from Mavio. Each request includes an HMAC-SHA256 signature in the `X-Mavio-Signature` header.

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

  def verify_webhook(payload_body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode(), payload_body, hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(f"sha256={expected}", signature)
  ```

  ```javascript Node.js theme={null}
  import crypto from 'crypto';

  function verifyWebhook(payloadBody, signature, secret) {
    const expected = crypto
      .createHmac('sha256', secret)
      .update(payloadBody)
      .digest('hex');
    return crypto.timingSafeEqual(
      Buffer.from(`sha256=${expected}`),
      Buffer.from(signature)
    );
  }
  ```
</CodeGroup>

<Warning>
  Always verify webhook signatures before processing the payload. Without verification, an attacker could send fake events to your endpoint.
</Warning>

## Retry logic

If your endpoint does not return a `2xx` response, Mavio retries the delivery with exponential backoff:

| Attempt   | Delay      |
| --------- | ---------- |
| 1st retry | 1 minute   |
| 2nd retry | 5 minutes  |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours    |
| 5th retry | 12 hours   |

After 5 failed attempts, the delivery is marked as failed. You can view and manually retry failed events from **Settings > Developer > Webhooks > \[Endpoint] > Deliveries**.

<Note>
  If an endpoint consistently fails (10+ consecutive failures), Mavio disables it and sends an email notification. Re-enable it from the webhook settings after fixing the issue.
</Note>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Endpoint not receiving events">
    Verify the URL is publicly accessible over HTTPS. Check that your server responds with a `200` status within 10 seconds. Use the **Send Test Event** button to debug.
  </Accordion>

  <Accordion title="Signature verification failing">
    Ensure you are comparing against the raw request body (not a parsed/re-serialized version). The signature is computed over the exact bytes sent by Mavio.
  </Accordion>

  <Accordion title="Duplicate events">
    Webhook deliveries are at-least-once. Your endpoint may receive the same event more than once during retries. Use the `X-Mavio-Delivery` header as an idempotency key to deduplicate.
  </Accordion>

  <Accordion title="What IP addresses do webhooks come from?">
    Mavio sends webhooks from a fixed set of IP addresses listed in the webhook settings page. You can allowlist these in your firewall.
  </Accordion>

  <Accordion title="Can I pause webhooks temporarily?">
    Yes. Toggle the **Active** switch on any endpoint to pause deliveries. Events that occur while paused are not queued and are skipped.
  </Accordion>
</AccordionGroup>
