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