Webhooks
Get notified when jobs finish and uploads arrive.
Add an endpoint in Settings → Developers → Webhooks (workspace owners only). Choose the events you want and copy the signing secret. It's shown once; you can rotate it later.
Events
| Event | When |
|---|---|
job.completed | A photo edit (enhancement or staging) finished. |
job.failed | A job failed. Its credits were refunded. |
video.completed | A Photo to Video or Listing Tour video finished. |
upload.completed | Photos arrived through an upload link and the user tapped Done. |
Payload
Every delivery is a POST with a JSON body:
{
"type": "job.completed",
"timestamp": "2026-10-05T10:15:00.000Z",
"data": {
"id": "9b1c…",
"kind": "enhancement",
"status": "completed",
"project_id": "2f4e…",
"photo_ids": ["a81d…"],
"enhancement_types": ["improve_lighting"],
"result": { "url": "https://…", "expires_at": "2026-10-06T10:15:00.000Z" }
}
}data for job events follows the Job shape, and its result URL lasts 24 hours. Events sent with Send test event include "test": true.
Verifying signatures
Deliveries are signed with the Standard Webhooks scheme, using the webhook-id, webhook-timestamp and webhook-signature headers. Verify against the raw request body.
import { Webhook } from "standardwebhooks";
const wh = new Webhook(process.env.PROPERTYPIXEL_WEBHOOK_SECRET!); // "whsec_…"
export async function POST(request: Request) {
const body = await request.text();
const event = wh.verify(body, Object.fromEntries(request.headers)); // throws if invalid
// handle event.type …
return new Response(null, { status: 204 });
}from standardwebhooks import Webhook
wh = Webhook(os.environ["PROPERTYPIXEL_WEBHOOK_SECRET"])
event = wh.verify(request.body, dict(request.headers)) # raises if invalidUse webhook-id to deduplicate: retries reuse the same ID.
Retries and disabling
- Respond with any
2xxwithin 10 seconds to acknowledge. - Failed deliveries are retried after 30 seconds, 2 minutes, 10 minutes, 30 minutes, 2 hours, 6 hours and 12 hours (8 attempts over about 21 hours).
- After 8 deliveries in a row exhaust their retries, or after 3 days of failures, the endpoint is disabled and the workspace owner is emailed. Re-enable it in Settings once it's fixed.
- The delivery log keeps 30 days of attempts, and you can resend any delivery.