Webhooks
Webhooks notify your application when events occur in AgentGate. Instead of polling for status, receive push notifications when runs complete.
Why Use Webhooks
Approach Pros Cons Polling Simple to implement Wastes API calls, delays Webhooks Real-time, efficient Requires endpoint setup
For production integrations, always use webhooks instead of polling.
Setting Up Webhooks
Create a Webhook
curl -X POST https://agentgate.mynewapi.com/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/agentgate",
"events": ["run.completed", "run.failed"],
"description": "Production webhook"
}'
Response:
{
"webhook" : {
"id" : "wh_abc123" ,
"url" : "https://your-app.com/webhooks/agentgate" ,
"events" : [ "run.completed" , "run.failed" ],
"enabled" : true ,
"description" : "Production webhook"
},
"secret" : "whsec_xxxxxxxxxxxxxxxxxxxxxxxx"
}
The webhook secret is only shown once at creation. Store it securely—you’ll need it for signature verification.
Endpoint Requirements
Your webhook endpoint must:
Use HTTPS (HTTP not allowed)
Be publicly accessible
Respond within 30 seconds
Return a 2xx status code for success
Event Types
Run Events
Event Description run.createdNew run created from work order run.startedRun execution began run.completedRun finished successfully run.failedRun terminated with failure run.cancelledRun cancelled by user
Verification Events
Event Description verification.startedVerification phase began verification.passedVerification checks passed verification.failedVerification checks failed
Common Subscriptions
// Basic: Just completion status
[ "run.completed" , "run.failed" ]
// Progress tracking
[ "run.started" , "run.completed" , "run.failed" , "run.cancelled" ]
// Full monitoring
[ "run.created" , "run.started" , "run.completed" , "run.failed" ,
"verification.started" , "verification.passed" , "verification.failed" ]
All webhooks follow this structure:
{
"id" : "evt_abc123" ,
"type" : "run.completed" ,
"timestamp" : "2024-01-15T10:35:00Z" ,
"data" : {
// Event-specific data
}
}
run.completed Payload
{
"id" : "evt_abc123" ,
"type" : "run.completed" ,
"timestamp" : "2024-01-15T10:35:00Z" ,
"data" : {
"runId" : "run_xyz789" ,
"workOrderId" : "wo_def456" ,
"status" : "succeeded" ,
"iterations" : 3 ,
"prUrl" : "https://github.com/org/repo/pull/42" ,
"timing" : {
"startedAt" : "2024-01-15T10:30:00Z" ,
"completedAt" : "2024-01-15T10:35:00Z" ,
"durationMs" : 300000
},
"cost" : {
"credits" : 150
},
"tenantContext" : {
"tenantId" : "acme-corp" ,
"tenantUserId" : "user_123"
}
}
}
run.failed Payload
{
"id" : "evt_abc124" ,
"type" : "run.failed" ,
"timestamp" : "2024-01-15T10:35:00Z" ,
"data" : {
"runId" : "run_xyz789" ,
"workOrderId" : "wo_def456" ,
"status" : "failed" ,
"iterations" : 10 ,
"error" : {
"code" : "MAX_ITERATIONS_REACHED" ,
"message" : "Run did not converge within maximum iterations"
},
"tenantContext" : {
"tenantId" : "acme-corp" ,
"tenantUserId" : "user_123"
}
}
}
Signature Verification
Always verify webhook signatures to ensure authenticity.
Webhooks include an X-AgentGate-Signature header:
X-AgentGate-Signature: sha256=abc123...
Verification Process
Get Raw Body
Access the raw request body before JSON parsing.
Compute HMAC
Calculate HMAC-SHA256 of the body using your webhook secret.
Compare Signatures
Use constant-time comparison to match computed vs received signature.
Verification Code
Node.js/Express
Python/Flask
import crypto from 'crypto' ;
import express from 'express' ;
const app = express ();
// Use raw body for signature verification
app . use ( '/webhooks/agentgate' , express . raw ({ type: 'application/json' }));
app . post ( '/webhooks/agentgate' , ( req , res ) => {
const signature = req . headers [ 'x-agentgate-signature' ];
const body = req . body ;
// Verify signature
const expectedSignature = 'sha256=' + crypto
. createHmac ( 'sha256' , process . env . WEBHOOK_SECRET )
. update ( body )
. digest ( 'hex' );
if ( ! crypto . timingSafeEqual (
Buffer . from ( signature ),
Buffer . from ( expectedSignature )
)) {
return res . status ( 401 ). send ( 'Invalid signature' );
}
// Process webhook
const event = JSON . parse ( body . toString ());
console . log ( 'Received:' , event . type );
res . sendStatus ( 200 );
});
Handling Webhooks
Best Practices
Return 200 immediately, then process asynchronously: app . post ( '/webhooks/agentgate' , async ( req , res ) => {
// Acknowledge immediately
res . sendStatus ( 200 );
// Process in background
processWebhook ( req . body ). catch ( console . error );
});
Handle duplicate deliveries gracefully using the event ID: async function processWebhook ( event ) {
// Check if already processed
if ( await isProcessed ( event . id )) {
return ; // Skip duplicate
}
// Process event
await handleEvent ( event );
// Mark as processed
await markProcessed ( event . id );
}
For reliability, queue webhooks for processing: app . post ( '/webhooks/agentgate' , async ( req , res ) => {
await queue . add ( 'agentgate-webhook' , req . body );
res . sendStatus ( 200 );
});
// Process from queue
queue . process ( 'agentgate-webhook' , async ( job ) => {
await handleEvent ( job . data );
});
Retry Behavior
If your endpoint fails, AgentGate retries:
Attempt Delay 1 Immediate 2 1 minute 3 5 minutes 4 30 minutes 5 2 hours
After 5 failed attempts, the delivery is abandoned.
What Triggers Retries
HTTP 4xx responses (except 410)
HTTP 5xx responses
Connection timeouts
Connection refused
Avoiding Retries
Return 200/201/204 promptly
Return 410 if you want to stop retries
Ensure endpoint is accessible
Testing Webhooks
Test Endpoint
Send a test event to verify configuration:
curl -X POST https://agentgate.mynewapi.com/v1/webhooks/wh_abc123/test \
-H "Authorization: Bearer YOUR_API_KEY"
Local Development
Use tunneling for local testing:
Start a tunnel (ngrok, localtunnel, etc.)
Create a webhook with your tunnel URL
Submit a work order
Receive webhook on localhost
Managing Webhooks
List Webhooks
curl https://agentgate.mynewapi.com/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY"
Update Webhook
curl -X PATCH https://agentgate.mynewapi.com/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["run.completed", "run.failed", "run.cancelled"],
"enabled": true
}'
Delete Webhook
curl -X DELETE https://agentgate.mynewapi.com/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"