Replicate
Verify and handle Replicate webhooks — prediction status updates from starting to succeeded, failed, or canceled.
Pass a webhook URL when you create a prediction, fetch your account’s
signing secret, and handle prediction statuses by name:
import { createWebhookHandler } from 'webhooks-sdk'
import { replicate } from 'webhooks-sdk/replicate'
const handler = createWebhookHandler({
provider: replicate({ secret: process.env.REPLICATE_WEBHOOK_SIGNING_SECRET! }),
on: {
succeeded: async (event) => {
await store(event.payload.id, event.payload.output)
},
failed: async (event) => {
await report(event.payload.id, event.payload.error)
},
},
})
export const POST = handler.fetch
The secret is account-wide rather than per-endpoint, and lives behind the API instead of a dashboard page:
curl -s -H "Authorization: Bearer $REPLICATE_API_TOKEN" \
https://api.replicate.com/v1/webhooks/default/secret
# {"key": "whsec_..."}
Replicate signs with Standard Webhooks
over the spec’s webhook-* headers — the id and timestamp are inside the
signed material, so a replayed request is rejected on its own.
Options
| Option | Type | Default | |
|---|---|---|---|
secret |
string | string[] |
— | The whsec_… signing secret(s). Pass an array during rotation. |
tolerance |
number |
300 |
Replay window in seconds. |
Events are statuses, not types
The webhook body is the prediction object itself — there is no type field.
The event name is the prediction’s status:
starting · processing · succeeded · failed · canceled — plus
aborted, for a prediction that exceeded its deadline before it could start.
These autocomplete in the on map; any other string still routes. Which
deliveries arrive is set per prediction by webhook_events_filter — start,
output, logs, completed — defaulting to output and completed.
output and logs fire repeatedly during the run, throttled to at most one
request per 500ms, so a handler for a non-terminal status must expect the
same status more than once; dedupe on event.id, which is unique per
delivery. Only the terminal delivery is retried on failure; intermediate
ones are not.
The payload is the whole prediction
Unlike vendors that send a thin pointer, Replicate delivers the prediction in
the same shape the predictions API returns — id, status, input,
output, error, logs, metrics, and the timestamps. No re-fetch needed:
succeeded: async (event) => {
const { id, output, metrics } = event.payload
},
The envelope
event.id— thewebhook-idheader, the spec’s canonical idempotency key, unique per delivery.event.type— the prediction’sstatus.event.timestamp— the signedwebhook-timestamp.event.payload— the prediction object; the result is atpayload.output.
Standalone & testing
The wrapper is the Standard Webhooks
provider with id: 'replicate' and
eventType: 'status', so the standalone triple lives there:
import {
verifyStandardWebhook, // (raw, { secret }) — throws on failure
parseStandardWebhook, // (raw, { eventType: 'status' }) — the envelope
signStandardWebhook, // (body, secret) — all three webhook-* headers, for tests
} from 'webhooks-sdk/standard-webhooks'
signStandardWebhook’s default webhook-* header names are exactly what
Replicate sends. See Testing.