Webhooks
Get notified when jobs complete instead of polling. Register a webhook URL and Masko sends a POST request with job results as soon as they are ready.
Create a Webhook
Register a webhook endpoint with the events you want to receive. The response includes a secret for verifying payloads.
/v1/webhookscurl -X POST https://api.masko.ai/v1/webhooks \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/api/masko-webhook",
"events": ["job.completed", "job.failed"]
}'
# Response:
# {
# "id": "wh_abc123",
# "url": "https://your-app.com/api/masko-webhook",
# "events": ["job.completed", "job.failed"],
# "secret": "whsec_k7x9m2p4q8r1...",
# "created_at": "2026-03-28T10:00:00Z"
# }Save the secret immediately. It cannot be retrieved later. If you lose it, delete the webhook and create a new one.
Webhook Payload
When a subscribed event fires, Masko sends a POST request to your URL with a JSON body:
{
"event": "job.completed",
"job_id": "job_abc123",
"type": "animation",
"collection_id": "col_xyz",
"asset_ids": {
"image": "ast_img_001",
"transparent_image": "ast_timg_001",
"video": "ast_vid_001",
"webm": "ast_webm_001",
"hevc": "ast_hevc_001"
},
"urls": {
"image": "https://assets.masko.ai/col_xyz/image.png",
"transparent_image": "https://assets.masko.ai/col_xyz/transparent.png",
"video": "https://assets.masko.ai/col_xyz/video.mp4",
"webm": "https://assets.masko.ai/col_xyz/video.webm",
"hevc": "https://assets.masko.ai/col_xyz/video_hevc.mp4"
},
"timestamp": "2026-03-28T10:01:32Z"
}Verify Signatures
Every webhook request includes a X-Masko-Signature header containing an HMAC-SHA256 signature of the request body. Verify it using the secret from when you created the webhook.
import crypto from 'crypto';
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body, 'utf-8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/api/masko-webhook', (req, res) => {
const signature = req.headers['x-masko-signature'];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
process.env.MASKO_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const { event, job_id, urls } = req.body;
console.log(`Job ${job_id} ${event}:`, urls);
res.status(200).send('OK');
});Retry Policy
If your endpoint returns a non-2xx status code or the request times out, Masko retries with exponential backoff:
- Retry 1: after 1 second
- Retry 2: after 5 seconds
- Retry 3: after 30 seconds
After 3 failed attempts, the delivery is marked as failed. If a webhook accumulates 100 consecutive delivery failures, it is automatically disabled. You can re-enable it by deleting and recreating it.
Manage Webhooks
List all your registered webhooks:
/v1/webhookscurl https://api.masko.ai/v1/webhooks \
-H "Authorization: Bearer masko_YOUR_API_KEY"
# Response:
# {
# "webhooks": [
# {
# "id": "wh_abc123",
# "url": "https://your-app.com/api/masko-webhook",
# "events": ["job.completed", "job.failed"],
# "created_at": "2026-03-28T10:00:00Z"
# }
# ]
# }Delete a webhook when you no longer need it:
/v1/webhooks/:idcurl -X DELETE https://api.masko.ai/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer masko_YOUR_API_KEY"
# Response: 204 No Content