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

# Webhooks for pre-recorded audio

Webhooks are custom HTTP callbacks that you can define to get notified when your pre-recorded audio transcripts are ready.

<Note>
  This guide covers webhooks for [pre-recorded audio
  transcription](/pre-recorded-audio). For webhooks with
  streaming audio, see [Webhooks for streaming
  speech-to-text](/streaming/webhooks).
</Note>

To use webhooks, you need to set up your own webhook receiver to handle webhook deliveries.

## Create a webhook for a transcription

<Tip>
  **Don't have a webhook endpoint yet?**

  Create a test webhook endpoint with [webhook.site](https://webhook.site) to
  test your webhook integration.
</Tip>

<Tabs>
  <Tab language="python" title="Python" default>
    To create a webhook, set the `webhook_url` parameter when you create a new transcription. The URL must be accessible from AssemblyAI's servers.

    ```python expandable theme={null}
    import requests
    import time

    base_url = "https://api.assemblyai.com"

    headers = {
        "authorization": "<YOUR_API_KEY>"
    }

    with open("./my-audio.mp3", "rb") as f:
      response = requests.post(base_url + "/v2/upload",
                              headers=headers,
                              data=f)

    upload_url = response.json()["upload_url"]

    data = {
        "audio_url": upload_url,
        "webhook_url": "https://example.com/webhook"
    }

    url = base_url + "/v2/transcript"
    response = requests.post(url, json=data, headers=headers)
    ```
  </Tab>

  <Tab language="python-sdk" title="Python SDK">
    To create a webhook, use `set_webhook()` on the transcription config. The URL must be accessible from AssemblyAI's servers.

    Use `submit()` instead of `transcribe()` to create a transcription without waiting for it to complete.

    ```python theme={null}
    import assemblyai as aai

    aai.settings.api_key = "<YOUR_API_KEY>"

    # audio_file = "./local_file.mp3"
    audio_file = "https://assembly.ai/wildfires.mp3"

    config = aai.TranscriptionConfig().set_webhook("https://example.com/webhook")

    aai.Transcriber().submit(audio_file, config)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    To create a webhook, set the `webhook_url` parameter when you create a new transcription. The URL must be accessible from AssemblyAI's servers.

    ```javascript expandable theme={null}
    import fs from "fs-extra";

    const baseUrl = "https://api.assemblyai.com";

    const headers = {
      authorization: "<YOUR_API_KEY>",
    };

    const path = "./my-audio.mp3";
    const audioData = await fs.readFile(path);
    let res = await fetch(`${baseUrl}/v2/upload`, {
      method: "POST",
      headers,
      body: audioData,
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const uploadResponse = await res.json();
    const uploadUrl = uploadResponse.upload_url;

    const data = {
      audio_url: uploadUrl,
      webhook_url: "https://example.com/webhook",
    };

    const url = `${baseUrl}/v2/transcript`;
    const response = await fetch(url, {
      method: "POST",
      headers: { ...headers, "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    if (!response.ok) throw new Error(`Error: ${response.status}`);
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    To create a webhook, include the `webhook_url` parameter when you create a new transcription. The URL must be accessible from AssemblyAI's servers.

    Use `submit()` instead of `transcribe()` to create a transcription without waiting for it to complete.

    ```javascript theme={null}
    import { AssemblyAI } from "assemblyai";

    const client = new AssemblyAI({
      apiKey: "<YOUR_API_KEY>",
    });

    // const audioFile = './local_file.mp3'
    const audioFile = "https://assembly.ai/wildfires.mp3";

    const params = {
      audio: audioFile,
      webhook_url: "https://example.com/webhook",
    };

    const run = async () => {
      const transcript = await client.transcripts.submit(params);
    };

    run();
    ```
  </Tab>
</Tabs>

## Handle webhook deliveries

When the transcript is ready, AssemblyAI will send a `POST` HTTP request to the URL that you specified.

Your webhook endpoint must return a 2xx HTTP status code within 10 seconds to indicate successful receipt. If a 2xx status is not received within 10 seconds, AssemblyAI will retry the webhook call up to a total of 10 attempts. If at any point your endpoint returns a 4xx status code, the webhook call is considered failed and will not be retried.

<Note>
  **Static Webhook IP addresses**

  AssemblyAI sends all webhook deliveries from fixed IP addresses:

  | Region | IP Address     |
  | ------ | -------------- |
  | US     | `44.238.19.20` |
  | EU     | `54.220.25.36` |
</Note>

### Delivery payload

The webhook delivery payload contains a JSON object with the following properties:

```json theme={null}
{
  "transcript_id": "5552493-16d8-42d8-8feb-c2a16b56f6e8",
  "status": "completed"
}
```

| Key             | Type   | Description                                                  |
| --------------- | ------ | ------------------------------------------------------------ |
| `transcript_id` | string | The ID of the transcript.                                    |
| `status`        | string | The status of the transcript. Either `completed` or `error`. |

The webhook payload only contains the `transcript_id` and `status`. It doesn't include the transcript text or error details. If the `status` is `"error"`, make a `GET /v2/transcript/{transcript_id}` request to retrieve the error message from the `error` field in the response. See [Retrieve a transcript with the transcript ID](#retrieve-a-transcript-with-the-transcript-id) below.

### Webhook status code

When you retrieve the transcript using the transcript ID, the JSON response will include a `webhook_status_code` field. This field contains the HTTP status code that AssemblyAI received when calling your webhook endpoint, allowing you to verify that the webhook was delivered successfully.

### Retrieve a transcript with the transcript ID

<Tabs>
  <Tab language="python" title="Python" default>
    ```python theme={null}
    import requests
    import time

    base_url = "https://api.assemblyai.com"

    headers = {
        "authorization": "<YOUR_API_KEY>"
    }

    transcript_id = "<TRANSCRIPT_ID>"

    polling_endpoint = f"https://api.assemblyai.com/v2/transcript/{transcript_id}"

    transcription_result = requests.get(polling_endpoint, headers=headers).json()

    if transcription_result['status'] == 'completed':
      print(f"Transcript ID: {transcript_id}")
    elif transcription_result['status'] == 'error':
      raise RuntimeError(f"Transcription failed: {transcription_result['error']}")

    ```
  </Tab>

  <Tab language="python-sdk" title="Python SDK">
    ```python theme={null}
    import assemblyai as aai

    aai.settings.api_key = "<YOUR_API_KEY>"

    transcript = aai.Transcript.get_by_id("<TRANSCRIPT_ID>")

    if transcript.status == "error":
      raise RuntimeError(f"Transcription failed: {transcript.error}")

    print(transcript.text)

    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    import fs from "fs-extra";

    const baseUrl = "https://api.assemblyai.com";

    const headers = {
      authorization: "<YOUR_API_KEY>",
    };

    const transcriptId = "<TRANSCRIPT_ID>";
    const pollingEndpoint = `${baseUrl}/v2/transcript/${transcriptId}`;

    let res = await fetch(pollingEndpoint, { headers });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const transcriptionResult = await res.json();

    if (transcriptionResult.status === "completed") {
      console.log(transcriptionResult.text);
    } else if (transcriptionResult.status === "error") {
      throw new Error(`Transcription failed: ${transcriptionResult.error}`);
    }
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK" default>
    ```javascript theme={null}
    import { AssemblyAI } from "assemblyai";

    const client = new AssemblyAI({
      apiKey: "<YOUR_API_KEY>",
    });

    const transcript = await client.transcripts.get("<TRANSCRIPT_ID>");

    if (transcript.status === "error") {
      throw new Error(`Transcription failed: ${transcript.error}`);
    }

    console.log(transcript.text);
    ```
  </Tab>
</Tabs>

## Authenticate webhook deliveries

You can authenticate webhook deliveries from AssemblyAI by including a custom HTTP header in the request.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    To add an authentication header, include the auth header name and value in `set_webhook()`.

    ```python {2} theme={null}
    config = aai.TranscriptionConfig().set_webhook(
        "https://example.com/webhook", "X-My-Webhook-Secret", "secret-value"
    )

    aai.Transcriber().submit(audio_url, config)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    To add an authentication header, include the `webhook_auth_header_name` and `webhook_auth_header_value` parameters.

    ```javascript {5-6} theme={null}
    client.transcripts.submit({
      audio:
        'https://assembly.ai/wildfires.mp3',
      webhook_url: 'https://example.com/webhook',
      webhook_auth_header_name: "X-My-Webhook-Secret",
      webhook_auth_header_value: "secret-value"
    })
    ```
  </Tab>
</Tabs>

<Tabs>
  <Tab language="python" title="Python" default>
    To add an authentication header, include the auth header name and value in `set_webhook()`.

    ```python expandable theme={null}
    import requests
    import time

    base_url = "https://api.assemblyai.com"

    headers = {
        "authorization": "<YOUR_API_KEY>"
    }

    with open("./my-audio.mp3", "rb") as f:
      response = requests.post(base_url + "/v2/upload",
                              headers=headers,
                              data=f)

    upload_url = response.json()["upload_url"]

    data = {
        "audio_url": upload_url,
        "webhook_url": "https://example.com/webhook",
        "webhook_auth_header_name": "X-My-Webhook-Secret",
        "webhook_auth_header_value": "secret-value"
    }

    url = base_url + "/v2/transcript"
    response = requests.post(url, json=data, headers=headers)
    ```
  </Tab>

  <Tab language="python-sdk" title="Python SDK">
    To add an authentication header, include the auth header name and value in `set_webhook()`.

    ```python theme={null}
    import assemblyai as aai

    aai.settings.api_key = "<YOUR_API_KEY>"

    # audio_file = "./local_file.mp3"
    audio_file = "https://assembly.ai/wildfires.mp3"

    config = aai.TranscriptionConfig().set_webhook("https://example.com/webhook", "X-My-Webhook-Secret", "secret-value")

    aai.Transcriber().submit(audio_file, config)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    To add an authentication header, include the `webhook_auth_header_name` and `webhook_auth_header_value` parameters.

    ```javascript expandable theme={null}
    import fs from "fs-extra";

    const baseUrl = "https://api.assemblyai.com";

    const headers = {
      authorization: "<YOUR_API_KEY>",
    };

    const path = "./my-audio.mp3";
    const audioData = await fs.readFile(path);
    let res = await fetch(`${baseUrl}/v2/upload`, {
      method: "POST",
      headers,
      body: audioData,
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const uploadResponse = await res.json();
    const uploadUrl = uploadResponse.upload_url;

    const data = {
      audio_url: uploadUrl,
      webhook_url: "https://example.com/webhook",
      webhook_auth_header_name: "X-My-Webhook-Secret",
      webhook_auth_header_value: "secret-value",
    };

    const url = `${baseUrl}/v2/transcript`;
    const response = await fetch(url, {
      method: "POST",
      headers: { ...headers, "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    if (!response.ok) throw new Error(`Error: ${response.status}`);
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    To add an authentication header, include the `webhook_auth_header_name` and `webhook_auth_header_value` parameters.

    ```javascript expandable theme={null}
    import { AssemblyAI } from "assemblyai";

    const client = new AssemblyAI({
      apiKey: "<YOUR_API_KEY>",
    });

    // const audioFile = './local_file.mp3'
    const audioFile = "https://assembly.ai/wildfires.mp3";

    const params = {
      audio: audioFile,
      webhook_url: "https://example.com/webhook",
      webhook_auth_header_name: "X-My-Webhook-Secret",
      webhook_auth_header_value: "secret-value",
    };

    const run = async () => {
      const transcript = await client.transcripts.submit(params);
    };

    run();
    ```
  </Tab>
</Tabs>

## Add metadata to webhook deliveries

To associate metadata for a specific transcription request, you can add your own query parameters to the webhook URL.

```plain theme={null}
https://example.com/webhook?customer_id=1234&order_id=5678
```

Now, when you receive the webhook delivery, you'll know the customer who requested it.

## Failed webhook deliveries

Webhook deliveries can fail for multiple reasons. For example, if your server is down or takes more than 10 seconds to respond.

If a webhook delivery fails, AssemblyAI will attempt to redeliver it up to 10 times, waiting 10 seconds between each attempt. If all attempts fail, AssemblyAI considers the delivery as permanently failed.
