SudoMock

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

Add, edit and rotate webhook endpoints from Dashboard → Webhooks. Each endpoint has its own signing secret (prefixed whsec_), shown once when you create or rotate it.

Events

EventWhen it fires
render.succeededAn image render job finished successfully.
render.failedAn image render job failed.
upload.succeededA PSD upload finished parsing into a mockup.
video.succeededA video job finished successfully.
video.failedA video job failed.
webhook.testFired 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

http
1X-SudoMock-Signature: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
2X-SudoMock-Timestamp: 1718900000
3Content-Type: application/json

Body

json
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.

Verify webhook signature
javascript
1import crypto from 'crypto'
2
3// 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')
8
9 // Reject replays older than 5 minutes.
10 const age = Math.floor(Date.now() / 1000) - Number(timestamp)
11 if (!timestamp || Math.abs(age) > 300) return false
12
13 const expected = crypto
14 .createHmac('sha256', secret)
15 .update(`${timestamp}.${rawBody}`)
16 .digest('hex')
17
18 return crypto.timingSafeEqual(
19 Buffer.from(signature || '', 'hex'),
20 Buffer.from(expected, 'hex')
21 )
22}

Always verify before trusting a payload

A request that fails verification must be rejected. Never act on a webhook body whose signature you have not validated against your endpoint's secret.

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}.

Webhooks | SudoMock Docs