Webhooks
WeaveOS sends webhook events to your server when workflow state changes, settlements complete, and disputes are raised or resolved.
Setup
Register a webhook endpoint in Dashboard → Developer → Webhooks. You can subscribe to individual event types or all events. Each endpoint has a unique signing secret used to verify that deliveries are genuine.
Endpoints must:
- Accept
POSTrequests with aContent-Type: application/jsonbody. - Respond with a
2xxstatus within 5 seconds. - Be accessible over HTTPS (HTTP endpoints are blocked in live mode).
Event object
{
"id": "evt_9a2k7f3p",
"type": "workflow.completed",
"created": 1716982330,
"data": {
"workflowId": "wf_e4rgffg44fg4g44",
"customerId": "cus_acme_prod_01",
"product": "ticket_resolution_v2",
"status": "Settled",
"margin": {
"achieved": 0.717,
"floor": 0.40
},
"settlement": {
"txId": "BjRVtMx3k9...sui",
"settledAt": "2026-05-21T14:32:10Z"
}
}
}Event types
| Event | Description |
|---|---|
workflow.started | Workflow moved to Executing state. |
workflow.completed | Workflow settled on-chain. Includes final margin and tx ID. |
workflow.failed | Workflow failed without a settleable outcome. |
quote.accepted | A quote was accepted and a workflow created against it. |
quote.expired | A quote expired before a workflow was created. |
settlement.paid | Payment transfer confirmed on-chain. |
dispute.raised | A dispute was opened within the dispute window. |
dispute.resolved | A dispute was resolved. Includes outcome (operator/customer/partial). |
Signature verification
Every delivery includes a weaveos-signature header containing a HMAC-SHA256 signature of the raw request body, signed with your endpoint's secret. Always verify this before processing an event.
Node.js
import express from "express";
const app = express();
app.post(
"/webhooks/weaveos",
express.raw({ type: "application/json" }),
(req, res) => {
let event;
try {
event = client.webhooks.construct(
req.body, // raw Buffer
req.headers["weaveos-signature"],
process.env.WEBHOOK_SECRET,
);
} catch (err) {
console.error("Signature verification failed:", err.message);
return res.status(400).send("Bad signature");
}
// Safe to process
switch (event.type) {
case "workflow.completed":
await handleSettlement(event.data);
break;
case "dispute.raised":
await notifyTeam(event.data);
break;
}
res.json({ received: true });
}
);Python
from flask import Flask, request
import weaveos
app = Flask(__name__)
@app.route("/webhooks/weaveos", methods=["POST"])
def webhook():
sig = request.headers.get("weaveos-signature")
try:
event = client.webhooks.construct(
request.data,
sig,
os.environ["WEBHOOK_SECRET"],
)
except weaveos.SignatureVerificationError as e:
return str(e), 400
if event.type == "workflow.completed":
handle_settlement(event.data)
return {"received": True}Manual verification
If you are not using an SDK, verify the signature manually:
import crypto from "crypto";
function verifyWebhook(rawBody, signatureHeader, secret) {
// Header format: "t=<timestamp>,v1=<signature>"
const parts = Object.fromEntries(signatureHeader.split(",").map(p => p.split("=")));
const payload = `${parts.t}.${rawBody}`;
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
if (expected !== parts.v1) throw new Error("Signature mismatch");
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) {
throw new Error("Timestamp too old (>5 min)");
}
}Retries
If your endpoint returns a non-2xx response or times out, WeaveOS retries delivery with exponential backoff:
| Attempt | Delay after previous attempt |
|---|---|
| 1st retry | 5 seconds |
| 2nd retry | 30 seconds |
| 3rd retry | 2 minutes |
| 4th retry | 10 minutes |
| 5th retry | 1 hour |
After 5 failed attempts, the delivery is marked as failed. You can manually replay failed deliveries from Dashboard → Developer → Webhooks → Delivery History.
Idempotency
Webhook deliveries may be sent more than once due to retries. Each event has a unique id field. Store processed event IDs and skip duplicate deliveries to ensure idempotent processing.