To prevent undesired requests to your webhooks, you can verify the webhook signature with a unique secret for your team. This secret can always be used to verify that the webhook sender is valid.

Why verify webhooks?

A webhook is an HTTP POST request from an API service to a URL you control. You might want to verify these requests to ensure they’re from pyannoteAI and not from a malicious actor.

How to verify webhooks

Each webhook request includes 2 HTTP headers that you can use to verify the request:

  • X-Signature: The base64 encoded signature of the webhook request.
  • X-Request-Timestamp: The timestamp of the webhook request in seconds since the Unix epoch.

1. Creating the signed content

As the webhook receiver, you need to create the signed content by concatenating the timestamp and the raw body of the request with a colon (:) and prefixing the result with v0:.

In code, this looks like:

Python
signed_content = f"v0:{timestamp}:{body}"

Make sure to use the raw body of the request (without headers or other metadata) before serializing to JSON or any other format.

2. Retrieving your webhook secret

You can find your webhook secret in the pyannoteAI dashboard.

  1. Sign in to your dashboard at https://dashboard.pyannote.ai
  2. Click on the “Webhooks” page in the sidebar.
  3. Click on the button with the eye icon to reveal your webhook secret.
  4. Copy the secret to your clipboard.

On the webhook page, you can also rotate your webhook secret. This is useful if you think your secret has been compromised.

3. Determining the expected signature

pyannoteAI uses the HMAC-SHA256 algorithm to sign webhook requests. To determine the expected signature, you need to:

  1. Create the signed content by concatenating the timestamp from the HTTP header and the raw body with a colon (:), then prefix the result with v0:.
  2. Compute the HMAC-SHA256 hash of the result using your webhook secret as the key.
  3. Base64 encode the result.

In code, this looks like:

Python
import hmac
import base64
import hashlib

def compute_signature(timestamp, body, secret):
    signed_content = f"v0:{timestamp}:{body}"
    signature = hmac.new(
        key=secret.encode('utf-8'),
        msg=signed_content.encode('utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()
    return signature

4. Verifying the signature

Then, simply compare the computed signature with the signature you received in the X-Signature header.

Python
computed_signature = compute_signature(timestamp, body, secret)
signature = request.headers.get("X-Signature")

if computed_signature == signature:
    print("The signature is valid")
else:
    print("The signature is invalid")
Reject the request if the signature is invalid.

Example FastAPI server

Here’s an example of how you can verify a webhook request in a FastAPI server:

Python
import os
import hmac
import hashlib
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

# Get key from your environment or edit default value
key = os.getenv("PYANNOTEAI_WEBHOOK_SIGNING_SECRET", "whs_....")


@app.post("/webhook")
async def validate_signature(request: Request):
    body = await request.body()
    headers = request.headers
    timestamp = headers.get("x-request-timestamp")
    received_signature = headers.get("x-signature")

    if not timestamp or not received_signature:
        raise HTTPException(status_code=400, detail="Missing headers")

    signed_content = f"v0:{timestamp}:{body.decode('utf-8')}"
    calculated_signature = hmac.new(
        key=key.encode("utf-8"),
        msg=signed_content.encode("utf-8"),
        digestmod=hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(calculated_signature, received_signature):
        raise HTTPException(status_code=403, detail="Invalid signature")

    # Do something with the payload, now that we know it's valid

    return {"status": "success"}