Webhooks
Subscribe to real-time events for photo enhancements, credit purchases, and more. Build async workflows with secure, reliable webhook delivery.
How webhooks work
Receive HTTP POST requests to your server when events occur
Configure endpoint
Add your webhook URL in the dashboard and select which events to receive
Receive events
We send HTTP POST requests to your endpoint when subscribed events occur
Process & respond
Handle the event in your app and return a 200 status code
Event types
Subscribe to specific events or receive all events
photo.enhancement.startedCommonFired when a photo enhancement job begins processing
photo.enhancement.completedCommonFired when a photo is successfully enhanced and all formats are ready
photo.enhancement.failedRareFired when a photo enhancement fails (invalid image, processing error, etc.)
photo.deletedUncommonFired when a photo is deleted from your account
credit.purchasedUncommonFired when credits are added to your account
credit.depletedRareFired when your credit balance reaches zero
Example webhook payload
What your endpoint will receive for a photo.enhancement.completed event
{
"id": "evt_abc123xyz",
"type": "photo.enhancement.completed",
"createdAt": "2025-12-21T20:00:00Z",
"data": {
"photo": {
"id": "photo_abc123",
"status": "completed",
"creditsUsed": 1,
"original": {
"url": "https://cdn.foodphoto.ai/originals/abc123.jpg",
"width": 2000,
"height": 1500
},
"enhanced": {
"square": "https://cdn.foodphoto.ai/enhanced/abc123_sq.jpg",
"portrait": "https://cdn.foodphoto.ai/enhanced/abc123_pt.jpg",
"landscape": "https://cdn.foodphoto.ai/enhanced/abc123_ls.jpg"
},
"metadata": {
"preset": "delivery-optimized",
"processedAt": "2025-12-21T20:00:00Z",
"processingTime": 2.4
}
}
}
}Webhook security
Verify webhook authenticity with HMAC signatures
Every webhook includes an X-FoodPhoto-Signature header containing an HMAC SHA-256 signature. Use your webhook signing secret to verify the request came from FoodPhoto.ai.
Verification Example (Node.js)
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const hmac = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(hmac)
);
}
// In your webhook handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-foodphoto-signature'];
const isValid = verifyWebhook(req.body, signature, WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the webhook...
res.status(200).send('OK');
});Automatic retries
We automatically retry failed webhook deliveries with exponential backoff
| Attempt | Delay | Total elapsed |
|---|---|---|
| Attempt 1 | Immediately | 0s |
| Attempt 2 | 5 seconds | 5s |
| Attempt 3 | 30 seconds | 35s |
| Attempt 4 | 2 minutes | ~2m 35s |
| Attempt 5 | 10 minutes | ~12m 35s |
| Attempt 6 | 30 minutes | ~42m 35s |
| Attempt 7 | 1 hour | ~1h 42m 35s |
Successful delivery requirements
Your endpoint must return a 2xx status code within 5 seconds to be considered successful. Non-2xx responses or timeouts will trigger automatic retries.
Best practices
Tips for building reliable webhook handlers
Respond quickly
Return a 200 status immediately and process the webhook asynchronously with a queue
Handle duplicates
Use the event ID to ensure idempotency in case we retry a successful delivery
Verify signatures
Always validate the HMAC signature to ensure the webhook came from FoodPhoto.ai
Use HTTPS
Webhook endpoints must use HTTPS in production for security
Monitor failures
Check your dashboard webhook logs regularly to catch and fix delivery failures
Test webhooks locally
Use tools like ngrok or localtunnel to expose your local server and test webhook delivery before deploying to production.
# Expose localhost:3000 with ngrok
ngrok http 3000
# Then use the HTTPS URL in your webhook settings
https://abc123.ngrok.io/webhookReady to set up webhooks?
Configure your webhook endpoint in the dashboard