Replicate Webhooks
When to Use This Skill
- Setting up Replicate webhook handlers
- Debugging signature verification failures
- Understanding Replicate event types and payloads
- Handling prediction lifecycle events (start, output, logs, completed)
Essential Code (USE THIS)
Express Webhook Handler
const express = require('express');
const crypto = require('crypto');
const app = express();
// CRITICAL: Use express.raw() for webhook endpoint - Replicate needs raw body
app.post('/webhooks/replicate',
express.raw({ type: 'application/json' }),
async (req, res) => {
// Get webhook headers
const webhookId = req.headers['webhook-id'];
const webhookTimestamp = req.headers['webhook-timestamp'];
const webhookSignature = req.headers['webhook-signature'];
// Verify we have required headers
if (!webhookId || !webhookTimestamp || !webhookSignature) {
return res.status(400).json({ error: 'Missing required webhook headers' });
}
// Manual signature verification (recommended approach)
const secret = process.env.REPLICATE_WEBHOOK_SECRET; // whsec_xxxxx from Replicate
const signedContent = `${webhookId}.${webhookTimestamp}.${req.body}`;
try {
// Extract base64 secret after 'whsec_' prefix
const secretBytes = Buffer.from(secret.split('_')[1], 'base64');
const expectedSignature = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64');
// Replicate can send multiple signatures, check each one
const signatures = webhookSignature.split(' ').map(sig => {
const parts = sig.split(',');
return parts.length > 1 ? parts[1] : sig;
});
const isValid = signatures.some(sig => {
try {
return crypto.timingSafeEqual(
Buffer.from(sig),
Buffer.from(expectedSignature)
);
} catch {
return false; // Different lengths = invalid
}
});
if (!isValid) {
return res.status(400).json({ error: 'Invalid signature' });
}
// Check timestamp to prevent replay attacks (5-minute window)
const timestamp = parseInt(webhookTimestamp, 10);
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - timestamp > 300) {
return res.status(400).json({ error: 'Timestamp too old' });
}
} catch (err) {
console.error('Signature verification error:', err);
return res.status(400).json({ error: 'Invalid signature' });
}
// Parse the verified webhook body
const prediction = JSON.parse(req.body.toString());
// Handle the prediction based on its status
console.log('Prediction webhook received:', {
id: prediction.id,
status: prediction.status,
version: prediction.version
});
switch (prediction.status) {
case 'starting':
console.log('Prediction starting:', prediction.id);
break;
case 'processing':
console.log('Prediction processing:', prediction.id);
if (prediction.logs) {
console.log('Logs:', prediction.logs);
}
break;
case 'succeeded':
console.log('Prediction completed successfully:', prediction.id);
console.log('Output:', prediction.output);
break;
case 'failed':
console.log('Prediction failed:', prediction.id);
console.log('Error:', prediction.error);
break;
case 'canceled':
console.log('Prediction canceled:', prediction.id);
break;
default:
console.log('Unknown status:', prediction.status);
}
res.status(200).json({ received: true });
}
);
Common Prediction Statuses
| Status |
Description |
Common Use Cases |
starting |
Prediction is initializing |
Show loading state in UI |
processing |
Model is running |
Display progress, show logs if available |
succeeded |
Prediction completed successfully |
Process final output, update UI |
failed |
Prediction encountered an error |
Show error message to user |
canceled |
Prediction was canceled |
Clean up resources, notify user |
Environment Variables
# Your webhook signing secret from Replicate
REPLICATE_WEBHOOK_SECRET=whsec_your_secret_here
Local Development
For local webhook testing, install the Hookdeck CLI:
Then start the tunnel:
npx hookdeck-cli listen 3000 replicate --path /webhooks/replicate
No account required. Provides local tunnel + web UI for inspecting requests.
Reference Materials
- What are Replicate webhooks — Event types and payload structure
- Setting up webhooks — Dashboard configuration and signing secret
- Signature verification — Verification algorithm and common issues
Resources for Implementation
Framework Examples
- Express implementation — Node.js with Express
- Next.js implementation — React framework with API routes
- FastAPI implementation — Python async framework
Documentation
Recommended: webhook-handler-patterns
Enhance your webhook implementation with these patterns:
Related Skills
1---2name: replicate-webhooks3description: Receive and verify Replicate webhooks. Use when setting up Replicate webhook handlers, debugging signature verification, or handling prediction events like start, output, logs, or completed.4license: MIT5---6
7# Replicate Webhooks
8
9## When to Use This Skill
10
11- Setting up Replicate webhook handlers
12- Debugging signature verification failures
13- Understanding Replicate event types and payloads
14- Handling prediction lifecycle events (start, output, logs, completed)
15
16## Essential Code (USE THIS)
17
18### Express Webhook Handler
19
20```javascript
21const express = require('express');
22const crypto = require('crypto');
23
24const app = express();
25
26// CRITICAL: Use express.raw() for webhook endpoint - Replicate needs raw body
27app.post('/webhooks/replicate',
28 express.raw({ type: 'application/json' }),
29 async (req, res) => {
30 // Get webhook headers
31 const webhookId = req.headers['webhook-id'];
32 const webhookTimestamp = req.headers['webhook-timestamp'];
33 const webhookSignature = req.headers['webhook-signature'];
34
35 // Verify we have required headers
36 if (!webhookId || !webhookTimestamp || !webhookSignature) {
37 return res.status(400).json({ error: 'Missing required webhook headers' });
38 }
39
40 // Manual signature verification (recommended approach)
41 const secret = process.env.REPLICATE_WEBHOOK_SECRET; // whsec_xxxxx from Replicate
42 const signedContent = `${webhookId}.${webhookTimestamp}.${req.body}`;
43
44 try {
45 // Extract base64 secret after 'whsec_' prefix
46 const secretBytes = Buffer.from(secret.split('_')[1], 'base64');
47 const expectedSignature = crypto
48 .createHmac('sha256', secretBytes)
49 .update(signedContent)
50 .digest('base64');
51
52 // Replicate can send multiple signatures, check each one
53 const signatures = webhookSignature.split(' ').map(sig => {
54 const parts = sig.split(',');
55 return parts.length > 1 ? parts[1] : sig;
56 });
57
58 const isValid = signatures.some(sig => {
59 try {
60 return crypto.timingSafeEqual(
61 Buffer.from(sig),
62 Buffer.from(expectedSignature)
63 );
64 } catch {
65 return false; // Different lengths = invalid
66 }
67 });
68
69 if (!isValid) {
70 return res.status(400).json({ error: 'Invalid signature' });
71 }
72
73 // Check timestamp to prevent replay attacks (5-minute window)
74 const timestamp = parseInt(webhookTimestamp, 10);
75 const currentTime = Math.floor(Date.now() / 1000);
76 if (currentTime - timestamp > 300) {
77 return res.status(400).json({ error: 'Timestamp too old' });
78 }
79 } catch (err) {
80 console.error('Signature verification error:', err);
81 return res.status(400).json({ error: 'Invalid signature' });
82 }
83
84 // Parse the verified webhook body
85 const prediction = JSON.parse(req.body.toString());
86
87 // Handle the prediction based on its status
88 console.log('Prediction webhook received:', {
89 id: prediction.id,
90 status: prediction.status,
91 version: prediction.version
92 });
93
94 switch (prediction.status) {
95 case 'starting':
96 console.log('Prediction starting:', prediction.id);
97 break;
98 case 'processing':
99 console.log('Prediction processing:', prediction.id);
100 if (prediction.logs) {
101 console.log('Logs:', prediction.logs);
102 }
103 break;
104 case 'succeeded':
105 console.log('Prediction completed successfully:', prediction.id);
106 console.log('Output:', prediction.output);
107 break;
108 case 'failed':
109 console.log('Prediction failed:', prediction.id);
110 console.log('Error:', prediction.error);
111 break;
112 case 'canceled':
113 console.log('Prediction canceled:', prediction.id);
114 break;
115 default:
116 console.log('Unknown status:', prediction.status);
117 }
118
119 res.status(200).json({ received: true });
120 }
121);
122```
123
124## Common Prediction Statuses
125
126| Status | Description | Common Use Cases |
127|--------|-------------|------------------|
128| `starting` | Prediction is initializing | Show loading state in UI |
129| `processing` | Model is running | Display progress, show logs if available |
130| `succeeded` | Prediction completed successfully | Process final output, update UI |
131| `failed` | Prediction encountered an error | Show error message to user |
132| `canceled` | Prediction was canceled | Clean up resources, notify user |
133
134## Environment Variables
135
136```bash
137# Your webhook signing secret from Replicate
138REPLICATE_WEBHOOK_SECRET=whsec_your_secret_here
139```
140
141## Local Development
142
143For local webhook testing, install the Hookdeck CLI:
144
145```bash
146```
147
148Then start the tunnel:
149
150```bash
151npx hookdeck-cli listen 3000 replicate --path /webhooks/replicate
152```
153
154No account required. Provides local tunnel + web UI for inspecting requests.
155
156## Reference Materials
157
158- [What are Replicate webhooks](references/overview.md) — Event types and payload structure
159- [Setting up webhooks](references/setup.md) — Dashboard configuration and signing secret
160- [Signature verification](references/verification.md) — Verification algorithm and common issues
161
162## Resources for Implementation
163
164### Framework Examples
165- [Express implementation](examples/express/) — Node.js with Express
166- [Next.js implementation](examples/nextjs/) — React framework with API routes
167- [FastAPI implementation](examples/fastapi/) — Python async framework
168
169### Documentation
170- [Official Replicate webhook docs](https://replicate.com/docs/topics/webhooks)
171- [Webhook setup guide](https://replicate.com/docs/topics/webhooks/setup-webhook)
172- [Webhook verification guide](https://replicate.com/docs/topics/webhooks/verify-webhook)
173
174## Recommended: webhook-handler-patterns
175
176Enhance your webhook implementation with these patterns:
177
178- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)
179- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)
180- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)
181- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)
182
183## Related Skills
184
185- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe payment webhooks
186- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub repository events
187- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify store events
188- [clerk-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/clerk-webhooks) - Clerk authentication events
189- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Idempotency, error handling, retry logic
190- [hookdeck-event-gateway](https://github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway) - Webhook infrastructure that replaces your queue — guaranteed delivery, automatic retries, replay, rate limiting, and observability for your webhook handlers