> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://murf.ai/api/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://murf.ai/api/docs/_mcp/server.

# Webhooks

If you have set a webhook URL while making the create job request, Murf Dub Automation API will send a webhook response to the specified URL when the job is completed, failed, or partially completed. The request to the webhook will contain the following information:

#### Headers

**`X-Signature-Timestamp`** `string`

The timestamp of when the request was sent to the webhook URL.

---

**`X-HMAC-Signature`** `string`

The HMAC signature of the payload.

---

#### Response

**`eventName`** `string`

The name of the event associated with the webhook response. This will be
"DUB\_JOB" for dubbing job endpoints.

---

**`data`** `object`

The data payload of the webhook response. The response structure will be
similar to the response received from Job Status API endpoint.

---

## Webhook Authentication and Validation

To ensure secure communication, all webhook requests sent by Murf Dub Automation API should be authenticated. The webhook payload includes an HMAC signature and a timestamp header that you can use to validate the request.

### Validating the Webhook

Use the following steps to validate the webhook:

1. Extract the `X-Signature-Timestamp` and `X-HMAC-Signature` headers from the request.
2. Concatenate the payload and the timestamp to form the data to be signed.
3. Compute the HMAC using the secret you included while sending the create job request and compare it with the `X-HMAC-Signature` header.
4. Ensure the timestamp is within an acceptable tolerance window to prevent replay attacks.

If validation fails, reject the webhook request and log the incident for further investigation.

This is how you can validate the webhook request using Murf Python SDK:

```py
from murf.utils import validate_hmac
import json

response_headers = {
  "X-Signature-Timestamp": "1744269464453",
  "X-HMAC-Signature": "the_hmac_signature_header_value"
}
response_data = {
    "eventName": "DUB_JOB",
    "data": {
        "project_id": None,
        "job_id": "THE_JOB_ID",
        "status": "COMPLETED",
        "download_details": [
            {
                "locale": "fr_FR",
                "status": "COMPLETED",
                "error_message": None,
                "download_url": "URL_TO_DOWNLOAD",
                "download_srt_url": "URL_TO_DOWNLOAD_SRT",
            }
        ],
        "credits_used": 1,
        "credits_remaining": 99,
        "failure_reason": None,
        "failure_code": None
    }
}

response_data_str = json.dumps(payload, separators=(',', ':'))

is_valid = validate_hmac(
    secret="the_secret_that_you_sent_in_create_job_request",
    payload=response_data_str,
    timestamp_header=response_headers["X-Signature-Timestamp"],
    hmac_signature=response_headers["X-HMAC-Signature"],
    tolerance_seconds=300
)
print(is_valid)
```

If you're not using python SDK, you can use the following python function to validate the webhook:

```py
import hmac
import hashlib
import time

def validate_hmac(secret, payload, timestamp_header, hmac_signature, tolerance_seconds):
    try:
        received_timestamp = int(timestamp_header)
        current_timestamp = int(time.time()) * 1000

        if (abs(current_timestamp - received_timestamp) / 1000) > tolerance_seconds:
            print("Timestamp is outside the allowed tolerance window.")
            return False

        data_to_sign = f"{payload}.{timestamp_header}"
        calculated_hmac = hmac.new(
            secret.encode('utf-8'),
            data_to_sign.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()

        return hmac.compare_digest(calculated_hmac, hmac_signature)
    except Exception as e:
        print(f"Error validating HMAC: {e}")
        return False
```