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:
# Install via npm
npm install -g hookdeck-cli
# Or via Homebrew
brew install hookdeck/hookdeck/hookdeck
Then start the tunnel:
hookdeck listen 3000 --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# Install via npm
147npm install -g hookdeck-cli
148
149# Or via Homebrew
150brew install hookdeck/hookdeck/hookdeck
151```
152
153Then start the tunnel:
154
155```bash
156hookdeck listen 3000 --path /webhooks/replicate
157```
158
159No account required. Provides local tunnel + web UI for inspecting requests.
160
161## Reference Materials
162
163- [What are Replicate webhooks](references/overview.md) — Event types and payload structure
164- [Setting up webhooks](references/setup.md) — Dashboard configuration and signing secret
165- [Signature verification](references/verification.md) — Verification algorithm and common issues
166
167## Resources for Implementation
168
169### Framework Examples
170- [Express implementation](examples/express/) — Node.js with Express
171- [Next.js implementation](examples/nextjs/) — React framework with API routes
172- [FastAPI implementation](examples/fastapi/) — Python async framework
173
174### Documentation
175- [Official Replicate webhook docs](https://replicate.com/docs/topics/webhooks)
176- [Webhook setup guide](https://replicate.com/docs/topics/webhooks/setup-webhook)
177- [Webhook verification guide](https://replicate.com/docs/topics/webhooks/verify-webhook)
178
179## Recommended: webhook-handler-patterns
180
181Enhance your webhook implementation with these patterns:
182
183- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)
184- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)
185- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)
186- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)
187
188## Related Skills
189
190- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe payment webhooks
191- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub repository events
192- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify store events
193- [clerk-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/clerk-webhooks) - Clerk authentication events
194- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Idempotency, error handling, retry logic
195- [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