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

# Maybloc webhooks: events, signatures and retries

> Get a signed POST when a transcript is queued, processing, completed, failed or canceled, or when add-ons are ready. How to verify the signature, and how failed deliveries are retried.

A webhook sends a POST to your server each time a transcript changes status, so you do not have to poll.

## How do I add a webhook endpoint?

Add an endpoint under [Webhooks](https://app.maybloc.com/developers/webhooks) in the console. The URL must be public HTTPS, redirects are not followed, and a workspace can have up to 10 endpoints. Copy the endpoint secret when it is shown.

## Which events are sent?

* `transcript.queued`
* `transcript.processing`
* `transcript.completed`
* `transcript.failed`
* `transcript.canceled`
* `transcript.addons.completed`
* `transcript.addons.failed`

## What does a delivery look like?

```json theme={null}
{
  "id": "evt_…",
  "type": "transcript.completed",
  "created_at": "2026-10-06T09:14:03Z",
  "data": { "id": "6f1c…", "object": "transcript", "model": "calico-1-flash", "status": "completed" }
}
```

Each request has three headers: `Maybloc-Timestamp` (a Unix timestamp), `Maybloc-Delivery` (a unique delivery id) and `Maybloc-Signature`.

## How do I verify the signature?

The signature is `sha256=` followed by the hex HMAC-SHA256 of the timestamp, a dot and the raw request body, keyed with your endpoint secret. Verify it on the raw body before parsing the JSON, compare in constant time, and reject timestamps older than 5 minutes.

```javascript Node.js theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody is the request body exactly as received, before JSON parsing.
export function verify(rawBody, headers, secret) {
  const timestamp = headers["maybloc-timestamp"];
  const signature = headers["maybloc-signature"] ?? "";
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = "sha256=" + createHmac("sha256", secret).update(timestamp + "." + rawBody).digest("hex");
  return signature.length === expected.length && timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
```

```python Python theme={null}
import hashlib, hmac, time

def verify(raw_body: bytes, headers, secret: str) -> bool:
    timestamp = headers["Maybloc-Timestamp"]
    signature = headers.get("Maybloc-Signature", "")
    if abs(time.time() - int(timestamp)) > 300:
        return False
    expected = "sha256=" + hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)
```

## How do I avoid processing a delivery twice?

Store the `Maybloc-Delivery` value of each delivery you handle and skip ids you have seen. A delivery can arrive more than once.

## What happens when my server is down?

A delivery that does not get a success response is retried up to 6 times in total, waiting 30 seconds, 2 minutes, 10 minutes, 30 minutes and 2 hours between attempts. After the last attempt it is marked failed. You can see recent deliveries in the console.


## Related topics

- [Maybloc API errors, rate limits and idempotency](https://docs.maybloc.com/guides/errors-and-limits.md)
- [Maybloc API documentation](https://docs.maybloc.com/index.md)
- [Transcribe a recording with the Maybloc API](https://docs.maybloc.com/quickstart.md)
- [How to authenticate Maybloc API requests](https://docs.maybloc.com/authentication.md)
- [Maybloc add-ons: redaction, summaries, sentiment and translation](https://docs.maybloc.com/guides/add-ons.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.