Receive project notifications
Configure a receiver, verify notifications and test your workflow when a CaptionBolt project changes.
Prepare your receiver
Webhooks send an HTTP request to your application when a video is ready for review, an export finishes, or a project fails or is cancelled. Integrations are available on Max subscriptions.
You need a public HTTPS URL that accepts POST requests. Only port 443 is supported. Localhost and private-network addresses cannot receive deliveries directly, and CaptionBolt does not follow redirects. For local testing, use a public HTTPS tunnel to your receiver.
The examples use https://receiver.example/captionbolt; replace it with your actual receiver URL.
Add an endpoint
- Open Settings → Integrations.
- Enter your HTTPS endpoint URL and choose events.
- Select Add Webhook.
- Save the signing secret when it appears. It is shown only once.
Your receiver uses this signing secret to verify incoming notifications. It is separate from an API key.
You can also create an endpoint through REST. Set CAPTIONBOLT_API_KEY to a key with webhooks:manage, as described in the API guide:
curl --fail-with-body https://api.captionbolt.com/v1/webhooks \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"url":"https://receiver.example/captionbolt","events":["project.completed","project.failed"]}'
This returns the endpoint id and its signing secret. Store the secret securely in your receiver's environment.
Choose events
| Event | Meaning |
| --- | --- |
| project.ready | Video processing is complete; the project can be reviewed or exported |
| project.completed | The export is finished and results can be retrieved; selected by default |
| project.failed | Processing or export failed; selected by default |
| project.cancelled | The project was cancelled |
Events cover eligible video projects in your account, whether created on the website, through REST or through MCP. A new endpoint receives future events, not a replay of earlier project changes. Standalone Transcripts, deleted projects and projects outside their access window are excluded.
project.ready does not mean the video has been exported or reviewed by a person.
{
"id": "event-uuid",
"type": "project.completed",
"createdAt": "2026-09-27T12:00:00.000Z",
"data": {
"projectId": "project-uuid",
"status": "completed",
"stage": "export",
"resultUrl": "https://api.captionbolt.com/v1/projects/project-uuid/result"
}
}
Events contain metadata, not video files or caption text. data.resultUrl is an authenticated REST endpoint. Fetch it with an API key that has projects:read to obtain fresh download links, then download the files without sending the key. See result downloads.
Verify incoming requests
CaptionBolt uses Standard Webhooks. Verify the signature against the exact raw request body before parsing or acting on it. The headers are webhook-id, webhook-timestamp and webhook-signature.
Example Node.js receiver
This minimal receiver verifies signatures and logs the event ID and type so you can test delivery. This example uses Node.js 24; your receiver can use another language. Install the JavaScript verifier in your own receiver project:
npm install standardwebhooks
read -r -s -p 'CaptionBolt signing secret: ' CAPTIONBOLT_WEBHOOK_SECRET
export CAPTIONBOLT_WEBHOOK_SECRET
printf '\n'
Save the following as receiver.mjs:
import { createServer } from "node:http";
import { Webhook } from "standardwebhooks";
const secret = process.env.CAPTIONBOLT_WEBHOOK_SECRET;
if (!secret) throw new Error("Set CAPTIONBOLT_WEBHOOK_SECRET.");
const verifier = new Webhook(secret);
createServer(async (request, response) => {
if (request.method !== "POST" || request.url !== "/captionbolt") {
response.writeHead(404).end();
return;
}
let event;
try {
const chunks = [];
let size = 0;
for await (const chunk of request) {
size += chunk.length;
if (size > 65536) {
response.writeHead(413).end();
return;
}
chunks.push(chunk);
}
const rawBody = Buffer.concat(chunks).toString("utf8");
event = verifier.verify(rawBody, {
"webhook-id": request.headers["webhook-id"],
"webhook-timestamp": request.headers["webhook-timestamp"],
"webhook-signature": request.headers["webhook-signature"],
});
} catch {
response.writeHead(400).end("Invalid webhook");
return;
}
console.log("Verified event:", event.id, event.type);
response.writeHead(204).end();
}).listen(3000, "127.0.0.1");
Run node receiver.mjs. Expose its /captionbolt route through your HTTPS host or a public HTTPS tunnel. CaptionBolt must reach the public HTTPS URL, not the local address.
The verifier checks the signature and timestamp. Keep your server clock accurate. Do not parse and re-serialize JSON before verification, or log the signing secret or full request body.
For a production workflow, replace logging with durable acceptance of the event: deduplicate by id, persist the event or task before returning 2xx, and process it asynchronously. Return an error if persistence fails so CaptionBolt can retry. The example tests receipt only; it does not provide a durable queue or download results.
Send a test
- Start your receiver and configure its signing secret.
- Open the endpoint in Settings → Integrations and select Send test.
- Check Delivery history for a successful delivery.
- Confirm your receiver logged a verified
webhook.testevent.
Only one test can be in progress per endpoint. Wait until it finishes, then allow at least 60 seconds before sending another test. Manual delivery retries also have a cooldown; follow the returned retry interval.
Handle webhook.test separately from project events; it does not represent a completed video. Use an actual future project event to test result retrieval.
If delivery fails, check the public URL, signing secret, raw-body handling and receiver response. Acknowledge within 15 seconds. Non-2xx responses, connection errors or a missing acknowledgement trigger retries.
Handle retries and secret changes
Notifications can repeat and arrive out of order. Deduplicate by event ID and check current project status when your workflow needs the latest state.
CaptionBolt makes three automatic attempts: the initial request, a retry about one minute after the first failure, and another about five minutes after the second failure. After the third failure, automatic delivery stops. Fix the receiver and use Retry delivery in settings; this keeps the event ID and starts a new three-attempt budget. History is kept for 30 days.
Use Rotate secret when changing your signing secret. Store the new secret when shown and update your receiver. The old secret remains valid for 24 hours, and deliveries include signatures for both during that overlap. Another rotation is blocked until the overlap ends.
Disabling an endpoint stops pending deliveries; a request already in flight may finish. Deleting an endpoint also deletes its delivery history. Project deletion or access expiry stops pending project notifications. Webhooks do not extend the 14-day project access window, so retrieve and save results before it ends.