See the end‑to‑end flow in the Webhooks overview.
Installation
Install the required packages:Copy
Ask AI
pip install chunkr-ai fastapi uvicorn svix
Python (FastAPI)
This minimal FastAPI handler verifies the webhook signature using Svix and then callschunkr.tasks.parse.get(task_id)
.
Copy
Ask AI
from fastapi import FastAPI, Request, Response, status
from svix.webhooks import Webhook, WebhookVerificationError
from chunkr_ai import Chunkr
import os
app = FastAPI()
CHUNKR_WEBHOOK_SECRET = (
os.environ.get('CHUNKR_WEBHOOK_SECRET') or ''
) # starts with "whsec_"
chunkr = Chunkr()
@app.post('/chunkr/webhooks', status_code=status.HTTP_204_NO_CONTENT)
async def webhook_handler(request: Request) -> Response:
headers = request.headers
payload = await request.body() # Verify against the raw body
try:
svix_id = headers.get('svix-id')
svix_timestamp = headers.get('svix-timestamp')
svix_signature = headers.get('svix-signature')
if not svix_id or not svix_timestamp or not svix_signature:
return Response(status_code=status.HTTP_400_BAD_REQUEST)
svix_headers = {
'svix-id': svix_id,
'svix-timestamp': svix_timestamp,
'svix-signature': svix_signature,
}
msg = Webhook(CHUNKR_WEBHOOK_SECRET).verify(payload, svix_headers) # dict
except WebhookVerificationError:
return Response(status_code=status.HTTP_400_BAD_REQUEST)
task_id = msg.get('task_id')
status_str = msg.get('status')
# Fetch only once the task is completed
if task_id and status_str == 'Succeeded':
_task = chunkr.tasks.parse.get(task_id)
if _task.output is not None:
# Do something with _task (e.g., persist results)
for chunk in _task.output.chunks:
print(chunk.content)
# No content response for webhook receivers
return Response(status_code=status.HTTP_204_NO_CONTENT)
Copy
Ask AI
uvicorn main:app --reload --port 8000
TypeScript (Bun)
This minimal Bun server verifies the webhook signature using Svix and then callschunkr.tasks.parse.get(task_id)
.
Copy
Ask AI
import Chunkr from "chunkr-ai";
import { Webhook } from "svix";
const CHUNKR_WEBHOOK_SECRET = process.env.CHUNKR_WEBHOOK_SECRET || ""; // starts with "whsec_"
const chunkr = new Chunkr();
// Webhook Endpoint
async function handleWebhook(request: Request): Promise<Response> {
try {
const headers = request.headers;
const payload = await request.text();
// Extract Svix headers
const svixId = headers.get("svix-id");
const svixTimestamp = headers.get("svix-timestamp");
const svixSignature = headers.get("svix-signature");
if (!svixId || !svixTimestamp || !svixSignature) {
return new Response("Missing required headers", { status: 400 });
}
const svixHeaders = {
"svix-id": svixId,
"svix-timestamp": svixTimestamp,
"svix-signature": svixSignature,
};
// Verify the webhook
const wh = new Webhook(CHUNKR_WEBHOOK_SECRET);
const msg = wh.verify(payload, svixHeaders) as any;
const taskId = msg.task_id;
const status = msg.status;
// Fetch only once the task is completed
if (taskId && status === "Succeeded") {
const task = await chunkr.tasks.parse.get(taskId);
if (task.output) {
// Do something with task (e.g., persist results)
const parseOutput = task.output;
if (parseOutput.chunks) {
for (const chunk of parseOutput.chunks) {
console.log(chunk.content);
}
}
}
}
// No content response for webhook receivers
return new Response(null, { status: 204 });
} catch {
return new Response("Bad Request", { status: 400 });
}
}
function startServer() {
const server = Bun.serve({
port: process.env.PORT || 8000,
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/chunkr/webhooks" && request.method === "POST") {
return handleWebhook(request);
}
return new Response("Not Found", { status: 404 });
},
});
return server;
}
if (import.meta.main) {
startServer();
}
Copy
Ask AI
bun run main.ts