Webhooks
Call your server, n8n, Make or Zapier when a rule fires, and verify that the call really came from a worker you trust.
Send
await triggers.createRule({
name: 'Ping my server',
trigger: { on: 'block', fireAt: await triggers.toBlock(new Date(Date.now() + 10 * 60_000)) },
action: 'webhook',
url: 'https://example.com/hooks/arkiv',
message: '{rule} fired at {time}',
})The URL must be https://. It is public, so it must not carry a secret (ruleWarnings flags URLs
that look like they do). Public workers refuse to call addresses that resolve to private networks.
What arrives
A POST with a JSON body, signed by the worker's wallet:
{
"event": "0x5c1e…",
"rule": "0xab12…",
"ruleName": "Ping my server",
"trigger": "block",
"deadline": "712345",
"firedAt": "712345",
"message": "Ping my server fired at 2026-10-20 09:00 UTC",
"worker": "0xf856…"
}| Header | Meaning |
|---|---|
x-trg-sig | The worker's signature over the exact body text (EIP-191) |
x-trg-worker | The worker's address |
x-trg-rule | The rule's key |
x-trg-event | The same for every retry of one firing |
Verify
Check the signature against workers you trust before you act. Run this on your server, with the raw body exactly as received.
import { verifyWebhook } from 'arkiv-triggers'
export async function POST(req: Request) {
const raw = await req.text()
const check = await verifyWebhook(raw, req.headers.get('x-trg-sig'), TRUSTED_WORKERS)
if (!check.ok) return new Response(check.reason, { status: 401 })
const body = JSON.parse(raw)
// check.worker signed it; body.event is your de-duplication key
return new Response('ok')
}TRUSTED_WORKERS is a list of worker addresses, for example the public worker's address from
listWorkers(), or your own.
De-duplicate on the signed event
Delivery is exactly once in practice and at least once in the worst case (a receipt delayed past the
worker's lease). Keep the event values you have handled, and use the one inside the signed body: the
x-trg-event header is not covered by the signature.
Retries and timing
| Attempts | retries + 1 (default 4) |
| Timeout per attempt | 10 s |
| Back-off | 2 s, 4 s, 8 s… |
| Total budget | 30 s, so the receipt lands while the worker still holds its claim |
| Not retried | A 4xx other than 408 and 429 |
The receipt records result.code, result.latencyMs and result.attempts, or result.error.
n8n, Make, Zapier
Their webhook URLs are just webhooks. Paste the URL into the rule; add a verification step in your flow if it can run code, or treat the URL as public and act only on non-sensitive events.