Webhooks

Webhooks let Pure Frame notify your server the moment something happens — a video finishes processing, a job fails, your credit balance runs low — instead of you polling for it.

Verifying webhook requests (code examples)

Every delivery includes an X-Pureframe-Signature header. Verify it against the raw request body before trusting the payload.

Python (Flask)

1import hashlib
2import hmac
3from flask import Flask, request, abort
4
5app = Flask(__name__)
6WEBHOOK_SECRET = "whsec_..." # from the endpoint's creation response
7
8@app.post("/webhooks/pureframe")
9def handle_webhook():
10 body = request.get_data() # raw bytes — must match exactly what was signed
11 signature = request.headers.get("X-Pureframe-Signature", "")
12 expected = "sha256=" + hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
13
14 if not hmac.compare_digest(signature, expected):
15 abort(401)
16
17 event = request.get_json()
18 print(f"Received {event['event']} for {event['data']}")
19 return "", 200

Node.js (Express)

1const express = require("express");
2const crypto = require("crypto");
3
4const app = express();
5const WEBHOOK_SECRET = "whsec_...";
6
7// Use express.text() (or a raw body parser), not express.json(),
8// so the exact bytes that were signed are available for verification.
9app.post("/webhooks/pureframe", express.text({ type: "*/*" }), (req, res) => {
10 const signature = req.headers["x-pureframe-signature"] || "";
11 const expected = "sha256=" + crypto
12 .createHmac("sha256", WEBHOOK_SECRET)
13 .update(req.body)
14 .digest("hex");
15
16 if (signature !== expected) {
17 return res.status(401).send();
18 }
19
20 const event = JSON.parse(req.body);
21 console.log(`Received ${event.event} for`, event.data);
22 res.status(200).send();
23});

Creating a webhook endpoint

$curl -X POST https://api.pureframe.ai/v1/webhooks \
> -H "Authorization: Bearer pf_..." \
> -H "Content-Type: application/json" \
> -d '{ "url": "https://yourapp.com/webhooks/pureframe", "events": ["job.completed", "job.failed"] }'
1{
2 "data": {
3 "id": "wh_abc123",
4 "user_id": "usr_...",
5 "url": "https://yourapp.com/webhooks/pureframe",
6 "events": ["job.completed", "job.failed"],
7 "is_active": true,
8 "created_at": "2026-07-01T12:00:00Z",
9 "secret": "whsec_9f8e7d6c5b4a..."
10 }
11}

The secret field is only returned once, at creation. Store it — you’ll need it to verify incoming deliveries. There is no way to retrieve it again; delete the endpoint and create a new one if you lose it.

The target URL must be publicly reachable — Pure Frame rejects URLs pointing at private, loopback, or link-local addresses (e.g. 10.0.0.0/8, 127.0.0.1, 169.254.169.254) both when you register the endpoint and again at delivery time.

Other endpoint operations:

$# List your endpoints
$curl https://api.pureframe.ai/v1/webhooks -H "Authorization: Bearer pf_..."
$
$# Update an endpoint's URL, events, or active state
>curl -X PATCH https://api.pureframe.ai/v1/webhooks/wh_abc123 \
> -H "Authorization: Bearer pf_..." \
> -H "Content-Type: application/json" \
> -d '{ "is_active": false }'
>
># Delete an endpoint
>curl -X DELETE https://api.pureframe.ai/v1/webhooks/wh_abc123 -H "Authorization: Bearer pf_..."

Event types

EventFires when
job.completedA video finishes processing and becomes searchable
job.failedA video processing job fails
video.createdA new video is uploaded and a job is queued
video.deletedA video is deleted
credits.lowAccount credit balance drops below the low-balance threshold
credits.exhaustedAccount credit balance reaches zero
account.deletedA user’s account is permanently deleted

Subscribe to only the events you need — deliveries for unsubscribed events are never sent to that endpoint.

Handling requests & retry logic

Every delivery is a POST with this envelope:

1{
2 "id": "evt_1a2b3c4d5e6f",
3 "event": "job.completed",
4 "created_at": "2026-07-01T12:03:45Z",
5 "data": { "job_id": "job_xyz789", "video_id": "vid_def456" }
6}

Return any 2xx status to acknowledge receipt. If your endpoint doesn’t respond successfully, Pure Frame retries up to 2 more times — once after 30 seconds, once after 60 seconds — then stops and marks the delivery failed. There is no long-tail retry window; if your endpoint might be briefly unavailable for longer than that, use the manual redelivery endpoint below to recover missed events.

Testing & signature verification

Fire a synthetic test event at any endpoint without waiting for a real one:

$curl -X POST https://api.pureframe.ai/v1/webhooks/wh_abc123/test \
> -H "Authorization: Bearer pf_..."

This delivers a webhook.ping event through the same signing and delivery path as real events, so it’s a reliable way to confirm your signature verification code works end to end.

Inspect delivery history for an endpoint:

$curl https://api.pureframe.ai/v1/webhooks/wh_abc123/deliveries \
> -H "Authorization: Bearer pf_..."

Manually redeliver a specific failed delivery (e.g. after fixing an outage on your end) instead of waiting for the automatic retry window:

$curl -X POST https://api.pureframe.ai/v1/webhooks/wh_abc123/deliveries/del_xyz789/retry \
> -H "Authorization: Bearer pf_..."

Plan limits

PlanMax active endpoints
Free3
Pro20