Skip to main content

Webhooks

Webhooks notify your application when events occur in AgentGate. Instead of polling for status, receive push notifications when runs complete.

Why Use Webhooks

ApproachProsCons
PollingSimple to implementWastes API calls, delays
WebhooksReal-time, efficientRequires 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

EventDescription
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

EventDescription
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"]

Payload Format

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.

Signature Header

Webhooks include an X-AgentGate-Signature header:
X-AgentGate-Signature: sha256=abc123...

Verification Process

1

Get Raw Body

Access the raw request body before JSON parsing.
2

Compute HMAC

Calculate HMAC-SHA256 of the body using your webhook secret.
3

Compare Signatures

Use constant-time comparison to match computed vs received signature.

Verification Code

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:
AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 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:
  1. Start a tunnel (ngrok, localtunnel, etc.)
  2. Create a webhook with your tunnel URL
  3. Submit a work order
  4. 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"