replicate-webhooks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReplicate Webhooks
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)
- 设置Replicate webhook处理器
- 调试签名验证失败问题
- 了解Replicate事件类型和负载
- 处理预测生命周期事件(启动、输出、日志、完成)
Essential Code (USE THIS)
核心代码(请使用此代码)
Express Webhook Handler
Express Webhook处理器
javascript
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 });
}
);javascript
const express = require('express');
const crypto = require('crypto');
const app = express();
// 关键:对webhook端点使用express.raw() - Replicate需要原始请求体
app.post('/webhooks/replicate',
express.raw({ type: 'application/json' }),
async (req, res) => {
// 获取webhook头部信息
const webhookId = req.headers['webhook-id'];
const webhookTimestamp = req.headers['webhook-timestamp'];
const webhookSignature = req.headers['webhook-signature'];
// 验证是否存在必要的头部
if (!webhookId || !webhookTimestamp || !webhookSignature) {
return res.status(400).json({ error: '缺少必要的webhook头部' });
}
// 手动签名验证(推荐方式)
const secret = process.env.REPLICATE_WEBHOOK_SECRET; // 来自Replicate的whsec_xxxxx
const signedContent = `${webhookId}.${webhookTimestamp}.${req.body}`;
try {
// 提取'whsec_'前缀后的base64密钥
const secretBytes = Buffer.from(secret.split('_')[1], 'base64');
const expectedSignature = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64');
// Replicate可能发送多个签名,逐一检查
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; // 长度不同 = 无效
}
});
if (!isValid) {
return res.status(400).json({ error: '无效签名' });
}
// 检查时间戳以防止重放攻击(5分钟窗口)
const timestamp = parseInt(webhookTimestamp, 10);
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - timestamp > 300) {
return res.status(400).json({ error: '时间戳过期' });
}
} catch (err) {
console.error('签名验证错误:', err);
return res.status(400).json({ error: '无效签名' });
}
// 解析已验证的webhook请求体
const prediction = JSON.parse(req.body.toString());
// 根据预测状态进行处理
console.log('收到预测webhook:', {
id: prediction.id,
status: prediction.status,
version: prediction.version
});
switch (prediction.status) {
case 'starting':
console.log('预测启动中:', prediction.id);
break;
case 'processing':
console.log('预测处理中:', prediction.id);
if (prediction.logs) {
console.log('日志:', prediction.logs);
}
break;
case 'succeeded':
console.log('预测成功完成:', prediction.id);
console.log('输出:', prediction.output);
break;
case 'failed':
console.log('预测失败:', prediction.id);
console.log('错误:', prediction.error);
break;
case 'canceled':
console.log('预测已取消:', prediction.id);
break;
default:
console.log('未知状态:', prediction.status);
}
res.status(200).json({ received: true });
}
);Common Prediction Statuses
常见预测状态
| Status | Description | Common Use Cases |
|---|---|---|
| Prediction is initializing | Show loading state in UI |
| Model is running | Display progress, show logs if available |
| Prediction completed successfully | Process final output, update UI |
| Prediction encountered an error | Show error message to user |
| Prediction was canceled | Clean up resources, notify user |
| 状态 | 描述 | 常见用例 |
|---|---|---|
| 预测正在初始化 | 在UI中显示加载状态 |
| 模型正在运行 | 显示进度,如有可用日志则展示 |
| 预测成功完成 | 处理最终输出,更新UI |
| 预测遇到错误 | 向用户显示错误信息 |
| 预测已取消 | 清理资源,通知用户 |
Environment Variables
环境变量
bash
undefinedbash
undefinedYour webhook signing secret from Replicate
来自Replicate的webhook签名密钥
REPLICATE_WEBHOOK_SECRET=whsec_your_secret_here
undefinedREPLICATE_WEBHOOK_SECRET=whsec_your_secret_here
undefinedLocal Development
本地开发
For local webhook testing, install the Hookdeck CLI:
bash
undefined如需进行本地webhook测试,请安装Hookdeck CLI:
bash
undefinedInstall via npm
通过npm安装
npm install -g hookdeck-cli
npm install -g hookdeck-cli
Or via Homebrew
或通过Homebrew安装
brew install hookdeck/hookdeck/hookdeck
Then start the tunnel:
```bash
hookdeck listen 3000 --path /webhooks/replicateNo account required. Provides local tunnel + web UI for inspecting requests.
brew install hookdeck/hookdeck/hookdeck
然后启动隧道:
```bash
hookdeck listen 3000 --path /webhooks/replicate无需账号。提供本地隧道+Web UI用于检查请求。
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
- 什么是Replicate webhook — 事件类型和负载结构
- 设置webhook — 仪表盘配置和签名密钥
- 签名验证 — 验证算法和常见问题
Resources for Implementation
实现资源
Framework Examples
框架示例
- Express implementation — Node.js with Express
- Next.js implementation — React framework with API routes
- FastAPI implementation — Python async framework
- Express实现 — Node.js + Express
- Next.js实现 — React框架 + API路由
- FastAPI实现 — Python异步框架
Documentation
文档
Recommended: webhook-handler-patterns
推荐:webhook-handler-patterns
Enhance your webhook implementation with these patterns:
Related Skills
相关技能
- stripe-webhooks - Stripe payment webhooks
- github-webhooks - GitHub repository events
- shopify-webhooks - Shopify store events
- clerk-webhooks - Clerk authentication events
- webhook-handler-patterns - Idempotency, error handling, retry logic
- hookdeck-event-gateway - Webhook infrastructure that replaces your queue — guaranteed delivery, automatic retries, replay, rate limiting, and observability for your webhook handlers
- stripe-webhooks - Stripe支付webhook
- github-webhooks - GitHub仓库事件
- shopify-webhooks - Shopify店铺事件
- clerk-webhooks - Clerk认证事件
- webhook-handler-patterns - 幂等性、错误处理、重试逻辑
- hookdeck-event-gateway - 替代队列的webhook基础设施——为你的webhook处理器提供可靠交付、自动重试、重放、速率限制和可观测性