Webhooks
Steward delivers real-time event notifications to your configured webhook endpoints. Every transaction state change, policy violation, and spend threshold crossing fires a signed HTTP POST to your URL.
Base path: /webhooks
Auth: Tenant-level (X-Steward-Key)
Event Types
Event Fired when tx.pendingA transaction is queued for human approval tx.approvedA pending transaction is approved tx.deniedA pending transaction is denied tx.signedA transaction is signed (and optionally broadcast) spend.thresholdAn agent’s spend tracking threshold is crossed policy.violationA hard policy rejects a transaction
You can subscribe to any subset of events or omit events to receive all of them.
Register a Webhook
curl -X POST https://api.steward.fi/webhooks \
-H "X-Steward-Key: your-tenant-key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/steward",
"events": ["tx.pending", "tx.signed", "policy.violation"],
"description": "Main webhook for agent notifications",
"maxRetries": 5,
"retryBackoffMs": 60000
}'
Response:
{
"ok" : true ,
"data" : {
"id" : "wh_550e8400-..." ,
"tenantId" : "your-tenant" ,
"url" : "https://your-app.com/webhooks/steward" ,
"events" : [ "tx.pending" , "tx.signed" , "policy.violation" ],
"secret" : "whsec_a1b2c3d4..." ,
"enabled" : true ,
"maxRetries" : 5 ,
"retryBackoffMs" : 60000 ,
"description" : "Main webhook for agent notifications" ,
"createdAt" : "2026-03-27T00:00:00Z"
}
}
The secret is only returned on creation. Store it securely — you’ll need it to verify incoming webhook signatures.
Body fields:
Field Required Description url✅ HTTPS endpoint to receive events events— Array of event types to subscribe to (default: all) description— Human-readable label maxRetries— Max delivery attempts on failure (0–10, default: 5) retryBackoffMs— Milliseconds between retries (min: 1000, default: 60000)
List Webhooks
curl https://api.steward.fi/webhooks \
-H "X-Steward-Key: your-tenant-key"
Returns all webhooks for the tenant. The secret field is omitted from list responses.
Update a Webhook
curl -X PUT https://api.steward.fi/webhooks/wh_550e8400-... \
-H "X-Steward-Key: your-tenant-key" \
-H "Content-Type: application/json" \
-d '{
"enabled": false,
"events": ["tx.pending", "tx.approved", "tx.denied"]
}'
Updatable fields: url, events, enabled, description, maxRetries, retryBackoffMs.
Delete a Webhook
curl -X DELETE https://api.steward.fi/webhooks/wh_550e8400-... \
-H "X-Steward-Key: your-tenant-key"
Delivery History
GET /webhooks/:id/deliveries?limit= 50 & offset = 0
Returns recent delivery attempts with status, response code, and error details:
{
"ok" : true ,
"data" : [
{
"id" : "del_..." ,
"webhookId" : "wh_..." ,
"event" : "tx.signed" ,
"status" : "delivered" ,
"attempts" : 1 ,
"responseStatus" : 200 ,
"createdAt" : "2026-03-27T12:34:56Z"
},
{
"id" : "del_..." ,
"event" : "tx.pending" ,
"status" : "failed" ,
"attempts" : 5 ,
"lastError" : "Connection refused" ,
"nextRetryAt" : null
}
]
}
Retry a Failed Delivery
POST /webhooks/deliveries/:id/retry
Resets the delivery to pending with 0 attempts and schedules an immediate re-delivery.
All events share the same envelope:
{
"id" : "evt_abc123" ,
"event" : "tx.signed" ,
"tenantId" : "your-tenant" ,
"agentId" : "agent-1" ,
"timestamp" : "2026-03-27T12:34:56.789Z" ,
"data" : { ... }
}
Event-specific data fields:
{
"approvalId" : "appr_..." ,
"txId" : "tx_..." ,
"to" : "0xSomeAddress" ,
"value" : "500000000000000000" ,
"chainId" : 8453 ,
"policyResults" : [
{ "policyId" : "auto-threshold" , "type" : "auto-approve-threshold" , "passed" : false , "reason" : "Value exceeds threshold" }
]
}
{
"txId" : "tx_..." ,
"txHash" : "0xabc..." ,
"to" : "0xSomeAddress" ,
"value" : "10000000000000000" ,
"chainId" : 8453 ,
"broadcast" : true
}
{
"txId" : "tx_..." ,
"to" : "0xBlockedAddress" ,
"value" : "1000000000000000000" ,
"failedPolicies" : [
{ "policyId" : "whitelist" , "type" : "approved-addresses" , "passed" : false , "reason" : "Address not in whitelist" }
]
}
Verifying Signatures
Every delivery includes an X-Steward-Signature header. Verify it to ensure the payload came from Steward:
import { createHmac } from "crypto" ;
function verifyWebhookSignature (
payload : string ,
signature : string ,
secret : string
) : boolean {
const expected = createHmac ( "sha256" , secret )
. update ( payload )
. digest ( "hex" );
return `sha256= ${ expected } ` === signature ;
}
// Express example
app . post ( "/webhooks/steward" , ( req , res ) => {
const sig = req . headers [ "x-steward-signature" ] as string ;
const body = JSON . stringify ( req . body );
if ( ! verifyWebhookSignature ( body , sig , process . env . WEBHOOK_SECRET ! )) {
return res . status ( 401 ). send ( "Invalid signature" );
}
const { event , agentId , data } = req . body ;
if ( event === "tx.pending" ) {
// Notify your team to review the pending transaction
await notifyAdmin ( agentId , data . approvalId , data . value );
}
res . json ({ ok: true });
});
Delivery Guarantees
Steward delivers with at-least-once semantics — design your handler to be idempotent
Failed deliveries are retried up to maxRetries times with retryBackoffMs spacing
Your endpoint should respond 2xx within 10 seconds or the attempt is counted as failed
Delivery history is kept for 30 days
Approvals — Review and act on tx.pending events
Tenant Config — Enable webhookCallbackEnabled in approval config