Webhooks
Receive a signed HTTPS request the moment a render, upload or video job finishes, with no polling loop required.
Overview
Register one or more endpoints in your dashboard and SudoMock will POST a signed JSON payload to them when your jobs complete. Webhooks are free on every plan. Polling GET /api/v1/jobs/{uuid} remains the source of truth, and webhooks are a convenience layer on top.
Manage endpoints in the dashboard
whsec_), shown once when you create or rotate it.Events
| Event | When it fires |
|---|---|
| render.succeeded | An image render job finished successfully. |
| render.failed | An image render job failed. |
| upload.succeeded | A PSD upload finished parsing into a mockup. |
| video.succeeded | A video job finished successfully. |
| video.failed | A video job failed. |
| webhook.test | Fired by the "Send test" action in the dashboard. |
Choose All events (an endpoint with no explicit event filter) to also receive events added in the future. The video.succeeded and video.failed events cover video jobs; render.succeeded, render.failed and upload.succeeded cover the async render and upload jobs you track with GET /api/v1/jobs/{render_uuid}. A failed upload is delivered as render.failed (there is no upload.failed event), so subscribe to render.failed if you ingest PSDs.
Payload & headers
Headers
1X-SudoMock-Signature: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a082X-SudoMock-Timestamp: 17189000003Content-Type: application/json
Body
1{2 "event": "render.succeeded",3 "render_uuid": "c315f78f-d2c7-4541-b240-a9372842de94",4 "kind": "render",5 "status": "succeeded",6 "result_url": "https://cdn.sudomock.com/renders/c315f78f.png",7 "error": null,8 "created_at": "2026-06-21T10:00:00Z"9}
Verifying signatures
Every delivery carries X-SudoMock-Signature and X-SudoMock-Timestamp. The signature is HMAC-SHA256(secret, "{timestamp}.{rawBody}"), hex-encoded. Always compute the HMAC over the raw request body (not a re-serialized object), compare it in constant time, and reject timestamps outside a small window to prevent replays.
1import crypto from 'crypto'23// Use the RAW request body (e.g. express.raw), not parsed JSON.4function verifySudoMockWebhook(req, secret) {5 const signature = req.header('X-SudoMock-Signature')6 const timestamp = req.header('X-SudoMock-Timestamp')7 const rawBody = req.body.toString('utf8')89 // Reject replays older than 5 minutes.10 const age = Math.floor(Date.now() / 1000) - Number(timestamp)11 if (!timestamp || Math.abs(age) > 300) return false1213 const expected = crypto14 .createHmac('sha256', secret)15 .update(`${timestamp}.${rawBody}`)16 .digest('hex')1718 return crypto.timingSafeEqual(19 Buffer.from(signature || '', 'hex'),20 Buffer.from(expected, 'hex')21 )22}
Always verify before trusting a payload
Retries & replay
Failed deliveries are retried automatically with exponential backoff. You can inspect every attempt (with its HTTP status, attempt count and error) and replay a single delivery from the deliveries panel in your dashboard. Deliveries are idempotent: handle the same render_uuid + event safely on your side. A replayed delivery may arrive with result_url set to null; when that happens, fetch the result by polling GET /api/v1/jobs/{render_uuid}.