---
title: "Receive Speak AI events with outbound webhook calls"
description: "Register a webhook so Speak AI posts events to your server, list and update the webhooks on your account, send a test payload, and delete a webhook."
---

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

# Receive Speak AI events with outbound webhook calls


import EndpointIndex from "@/components/api/EndpointIndex.astro";

The Speak AI API exposes 5 webhooks endpoints under the base URL `https://api.speakai.co/v1`. Every request needs the `x-speakai-key` and `x-access-token` headers described in [Authentication](/api/authentication/).

Receive Webhooks from Speak to your platform. Manage webhook workflows and select events.

## What can you do with the webhooks endpoints?

Speak AI groups these 5 endpoints under the webhooks resource. Each entry below links to the full reference for that endpoint further down this page.

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | [`/webhook`](#get-webhook) | List |
| `POST` | [`/webhook`](#post-webhook) | Create Webhook |
| `PUT` | [`/webhook/{webhookId}`](#put-webhook-webhook-id) | Update |
| `DELETE` | [`/webhook/{webhookId}`](#delete-webhook-webhook-id) | Delete |
| `POST` | [`/webhook/test/{webhookId}`](#post-webhook-test-webhook-id) | Test |

<h2 id="get-webhook">List</h2>

If you want to list all the exisitng webhooks

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X GET 'https://api.speakai.co/v1/webhook' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="get-webhook" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string |  |
| `data` | object |  |
| `data.totalCount` | integer | How many webhooks you have in total, so you can page through them. |
| `data.webhooks` | object[] |  |

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (List Webhooks), `application/json`. Arrays are shortened to one entry and long strings are cut.

```json
{
  "status": "success",
  "data": {
"totalCount": 1,
"webhooks": [
  {
    "events": [
      "media.created"
    ],
    "isActive": true,
    "_id": "60884fec4cb70a295c0ddf59",
    "callbackUrl": "https://example.com/webhooks/speak",
    "metaData": {
      "providedInternalUUID": "111222333"
    },
    "description": "This is an edited explanation.",
    "createdAt": "2021-04-27T17:54:52.250Z"
  }
]
  }
}
```

</div>

</ResponsePanel>

</div>

<h2 id="post-webhook">Create Webhook</h2>

Webhooks are the means by which you receive notification in the instant event you are subscribed to is triggered. We support several media events right now:

Speak will send you a `POST` request to the webhookUrl.

We will send you related keys in the `body` params such as `mediaId`, `recorderId`, `state`, `meetingAssistantId` or `meetingAssistantStatus`

**For Media Files:**

- `media.created`

- `media.analyzed`

- `media.reanalyzed`

- `media.failed`

- `media.deleted`

**For Text Files:**

- `text.created`

 - You will receive the foll

- `text.analyzed`

 - You will receive following keys: `mediaId`, `state`

- `text.reanalyzed`

 - You will receive following keys: `mediaId`, `state`

- `text.failed`

 - You wil receive following keys: `mediaId`

- `text.deleted`

 - You will receive the following keys: `mediaId`

**For Embed Recorder:**

- `embed_recorder.created`

 - You will receive following keys: `recorderId`

- `embed_recorder.deleted`

 - You will receive following keys: `recorderId`

- `embed_recorder.recording_received`

 - You will receive following keys: `recorderId`, `mediaId`

**For Meeting Assistant:**

- `meeting_assistant.status`

 - Trigger a webhook for meeting assistant status changes for following status: Joining call, Waiting room, In call not recording, In call recording, Call ended, Error.

 - You will receive following keys: **`mediaId, meetingAssistantId`**, and **`meetingAssistantStatus`**

**For AI Chat:**

- `chat.status`

 - AI Chat response with the final answer.

 - You will receive follwoing keys: `mediaIds, folderId, state, promptId, messageId and answer`

**For CSV Files:**

- `csv.uploaded`

 - When you upload the CSV file, it will return with `fileId, totalRecords, succeededRecords`

- `csv.failed`

 - When there is a failure or few records failed, you will receive fileId
 `fileId, totalRecords, failedRecords, failedRecordsList`

You can subscribe to one or more events at once for one media, and then later update your webhook as needed.
Internally, Speak AI system will `POST` to `callbackURL` with provided `metaData` as a request body, the attempt will be recorded and you can access the results, errors and response times in [https://app.speakai.co/developers/webhooks](https://app.speakai.co/developers/webhooks)

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `callbackUrl` | string, **required** | Give the URL Speak posts events to. required; validated as a plain string with no length cap and no uri format check |
| `events` | string[] | List the events you want this webhook to receive. optional. The array carries no.required, no.optional and no min or max item count; the schema keys are optional by default. Each item must be one of the 18 listed values, which come from the WebhookEvent list. The enum applies to the array items, not to the array itself. One of: `embed_recorder.created`, `embed_recorder.deleted`, `embed_recorder.recording_received`, `media.analyzed`, `media.created`, `media.deleted`, `media.failed`, `media.reanalyzed`, `media.updated`, `text.analyzed`, `text.created`, `text.deleted`, `text.failed`, `text.reanalyzed`, `meeting_assistant.status`, `chat.status`, `csv.uploaded`, `csv.failed`. |
| `metaData` | object | Attach your own data to the webhook. Speak merges this object into the body of every delivery for this webhook, then adds eventType and deliveryId and the fields for the event that fired. optional, unconstrained object, any keys accepted. Delivery body is built as \{.webhook.metaData, eventType, deliveryId \} plus event fields, so a metaData key named eventType, deliveryId, state, mediaId, mediaIds, folderId, recorderId, promptId, messageId, prompt, answer, meetingAssistantId or meetingAssistantStatus is overwritten by Speak's own value. |
| `mediaId` | string | Scope the webhook to a single media item instead of your whole account. optional, empty string allowed, no length cap |
| `description` | string | Describe what this webhook does on your side so you can recognize it later. optional, empty string allowed, no length cap |
| `source` | string | Say which tool is creating the webhook. Leave it out and the webhook is stored as "speak". optional; values come from the WebhookEventSource list. The schema.default('speak') is not what applies it: the controller passes source through untouched and the stored record defaults to 'speak'. One of: `speak`, `zapier`, `n8n`, `pipedream`, `make`. |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/webhook' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "callbackUrl": "https://example.com/webhooks/speak",
  "metaData": {
"providedInternalUUID": "111222333"
  },
  "events": [
"media.created",
"media.analyzed",
"text.created",
"text.analyzed",
"text.reanalyzed",
"text.failed",
"text.deleted",
"media.reanalyzed",
"media.failed",
"media.deleted"
  ],
  "description": "This is a readable explanation of what this webhook does on your side."
}'
```

</CodePanel>

<ResponsePanel id="post-webhook" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string |  |
| `data` | object |  |
| `data.webhookId` | string | The id of the webhook you just created. Store it, because you need it to update, test or delete the webhook. |

Example response (Success), `application/json`.

```json
{
  "status": "success",
  "data": {
"webhookId": "608850ed4cb70a295c0ddf5b"
  }
}
```

</div>

</ResponsePanel>

</div>

<h2 id="put-webhook-webhook-id">Update</h2>

You can update definition of an existing webhook: change events it's subscribed to, edit description or your `metaData`.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `webhookId` | path | string | Yes |  |

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `callbackUrl` | string, **required** | Give the URL Speak posts events to. Send it on every update, even when you are only changing something else. required; validated as a plain string with no length cap and no uri format check |
| `metaData` | object | Replace the data Speak merges into the body of every delivery for this webhook. Speak adds eventType, deliveryId and the fields for the event that fired on top of it. optional, unconstrained object, any keys accepted. Omitting it writes undefined over the stored value, because the controller passes the destructured value straight into findOneAndUpdate. |
| `mediaId` | string | Scope the webhook to a single media item instead of your whole account. optional, empty string allowed, no length cap |
| `events` | string[] | Replace the list of events this webhook receives. explicitly.optional; no minimum or maximum item count. Each item must be one of the 18 listed values, which come from WebhookEvent. One of: `embed_recorder.created`, `embed_recorder.deleted`, `embed_recorder.recording_received`, `media.analyzed`, `media.created`, `media.deleted`, `media.failed`, `media.reanalyzed`, `media.updated`, `text.analyzed`, `text.created`, `text.deleted`, `text.failed`, `text.reanalyzed`, `meeting_assistant.status`, `chat.status`, `csv.uploaded`, `csv.failed`. |
| `description` | string | Describe what this webhook does on your side so you can recognize it later. optional, empty string allowed, no length cap |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X PUT 'https://api.speakai.co/v1/webhook/609d7de35bde285f4c98ca63' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "callbackUrl": "https://example.com/webhooks/speak",
  "metaData": {
"providedInternalUUID": "777888999"
  },
  "events": [
"media.created",
"media.analyzed",
"media.deleted"
  ],
  "description": "This is an edited explanation."
}'
```

</CodePanel>

<ResponsePanel id="put-webhook-webhook-id" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type |
| --- | --- |
| `status` | string |
| `message` | string |

Example response (Successful), `application/json`.

```json
{
  "status": "success",
  "message": "Webhook updated"
}
```

</div>

</ResponsePanel>

</div>

<h2 id="delete-webhook-webhook-id">Delete</h2>

#### DELETE Webhook

This endpoint allows you to delete a specific webhook identified by its unique `webhookId`.

##### Request Parameters

- `webhookId` (path parameter): The unique identifier of the webhook you wish to delete.

##### Expected Response

Upon successful deletion of the webhook, the API will return a JSON object with the following structure:

- `status`: A string indicating the status of the request.

- `message`: A string providing additional information about the result of the operation.

##### Notes

- Ensure that the `webhookId` provided in the request is valid and corresponds to an existing webhook.

- Deleting a webhook is irreversible; once deleted, the webhook cannot be recovered.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `webhookId` | path | string | Yes |  |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X DELETE 'https://api.speakai.co/v1/webhook/609d7de35bde285f4c98ca63' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="delete-webhook-webhook-id" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type |
| --- | --- |
| `status` | string |
| `message` | string |

Example response (Success), `application/json`.

```json
{
  "status": "success",
  "message": "Webhook deleted!"
}
```

</div>

</ResponsePanel>

</div>

<h2 id="post-webhook-test-webhook-id">Test</h2>

Looking to test the webhook without uploading much media in your account ? Testing is made easier using this endpoint. You can trigger an event on a particular webhook and get the request\response or error.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `webhookId` | path | string | Yes |  |

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `event` | string, **required** | Pick the event to fire so you can see the request, the response and any error your endpoint returns. required; the 18 values come from the WebhookEvent list. No other body key is accepted, so any extra property returns a 400. One of: `embed_recorder.created`, `embed_recorder.deleted`, `embed_recorder.recording_received`, `media.analyzed`, `media.created`, `media.deleted`, `media.failed`, `media.reanalyzed`, `media.updated`, `text.analyzed`, `text.created`, `text.deleted`, `text.failed`, `text.reanalyzed`, `meeting_assistant.status`, `chat.status`, `csv.uploaded`, `csv.failed`. |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/webhook/test/659ee5c7350d7f6593fd36ea' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "event": "media.created"
}'
```

</CodePanel>

<ResponsePanel id="post-webhook-test-webhook-id" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type |
| --- | --- |
| `status` | string |
| `data` | object |
| `data.isFirstAttempt` | boolean |
| `data._id` | string |
| `data.webhookId` | string |
| `data.event` | string |
| `data.requestBody` | string |
| `data.responseBody` | string |
| `data.responseTime` | integer |
| `data.createdAt` | string (date-time) |
| `data.__v` | integer |

Example response (Success), `application/json`.

```json
{
  "status": "success",
  "data": {
"isFirstAttempt": true,
"_id": "608854d7368c102ad3d3ad53",
"webhookId": "60884fec4cb70a295c0ddf59",
"event": "media.created",
"requestBody": "{\"providedInternalUUID\":\"111222333\"}",
"responseBody": "{\"message\":\"My UUID is 111222333.\"}",
"responseTime": 48,
"createdAt": "2021-04-27T18:15:51.686Z",
"__v": 0
  }
}
```

Example response (Error), `application/json`.

```json
{
  "status": "success",
  "data": {
"isFirstAttempt": true,
"_id": "608854d7368c102ad3d3ad53",
"webhookId": "60884fec4cb70a295c0ddf59",
"event": "media.created",
"requestBody": "{\"providedInternalUUID\":\"111222333\"}",
"responseBody": "{\"message\":\"invalid json response body at https://example.com/webhooks/speak reason: Unexpected token < in JSON at position 0\",\"type\":\"invalid-json\"}",
"responseTime": 48,
"createdAt": "2021-04-27T18:15:51.686Z",
"__v": 0
  }
}
```

</div>

</ResponsePanel>

</div>

## Related pages

- [API reference](/api/) for the base URL, authentication, and the error format.
- [Authenticate with the Speak AI API using access tokens](/api/authentication/)
- [Upload audio and video to Speak AI and read insights](/api/media/)
- [Create and update live transcription sessions in Speak AI](/api/live-transcription/)
- [Analyze text notes with the Speak AI text endpoints](/api/text/)

Get an API key on the [Speak AI developer page](https://app.speakai.co/developers?utm_source=docs&utm_medium=referral&utm_campaign=api-reference&utm_content=api-webhooks).

Source: https://docs.speakai.co/api/webhooks/index.mdx
