- devops docs: 8 files updated for Phase 6 state; field-trial.md added (946-line runbook) - developer docs: api-reference (50+ endpoints), quick-start, 5 existing guides updated, 5 new guides added - engineering docs: all 12 files updated (services, architecture, SDK guide, testing, overview) - OpenSpec archives: phase-7-devops-field-trial, developer-docs-phase6-update, engineering-docs-phase6-update - VALIDATOR.md + scripts/start-validator.sh: V&V Architect tooling added - .gitignore: exclude session artifacts, build artifacts, and agent workspaces Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.9 KiB
Configure Webhooks
Webhooks let AgentIdP push real-time events to your application when agents, credentials, or tokens change state. This guide covers creating subscriptions, the available event types, delivery guarantees, and how to inspect delivery history.
Prerequisites
- A running AgentIdP instance
- A valid Bearer token with
organization_idin its claims - A publicly reachable HTTPS endpoint to receive events (for local development, use a tool like ngrok)
Available event types
| Event type | Triggered when |
|---|---|
agent.created |
A new agent is registered |
agent.updated |
An agent's metadata is updated |
agent.suspended |
An agent's status changes to suspended |
agent.reactivated |
An agent's status changes from suspended to active |
agent.decommissioned |
An agent is decommissioned |
credential.generated |
New credentials are created for an agent |
credential.rotated |
A credential's secret is rotated |
credential.revoked |
A credential is revoked |
token.issued |
An access token is issued |
token.revoked |
An access token is revoked |
Create a subscription
POST /api/v1/webhooks
curl -s -X POST http://localhost:3000/api/v1/webhooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "prod-agent-events",
"url": "https://my-app.example.com/hooks/sentryagent",
"events": ["agent.created", "agent.decommissioned", "token.issued"]
}' | jq .
Response (201 Created):
{
"id": "wh-1a2b3c4d-e5f6-7890-abcd-ef1234567890",
"organization_id": "org-0a1b2c3d-e4f5-6789-abcd-ef0123456789",
"name": "prod-agent-events",
"url": "https://my-app.example.com/hooks/sentryagent",
"events": ["agent.created", "agent.decommissioned", "token.issued"],
"active": true,
"signingSecret": "whsec_a1b2c3d4e5f6789...",
"failure_count": 0,
"created_at": "2026-04-04T09:00:00.000Z",
"updated_at": "2026-04-04T09:00:00.000Z"
}
Save the
signingSecretnow. It is shown once. Use it to verify the HMAC-SHA256 signature on incoming webhook requests. See "Verifying delivery signatures" below.
export WEBHOOK_ID="wh-1a2b3c4d-e5f6-7890-abcd-ef1234567890"
export SIGNING_SECRET="whsec_a1b2c3d4e5f6789..."
Webhook payload format
Every delivery sends a POST to your URL with Content-Type: application/json and this body:
{
"id": "evt-uuid-here",
"event": "agent.created",
"timestamp": "2026-04-04T09:00:00.000Z",
"organization_id": "org-0a1b2c3d-e4f5-6789-abcd-ef0123456789",
"data": {
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "screener-001@talent.ai",
"agentType": "screener"
}
}
The data object contains event-specific fields. For agent.* events it includes agent
metadata. For credential.* events it includes credentialId and agentId. For token.*
events it includes agentId and scope.
Verifying delivery signatures
AgentIdP signs every delivery with HMAC-SHA256 using your signingSecret. The signature is
in the X-SentryAgent-Signature header as sha256=<hex-digest>.
Verify it in Node.js:
const crypto = require('crypto');
function verifySignature(rawBody, signingSecret, signatureHeader) {
const expected = 'sha256=' + crypto
.createHmac('sha256', signingSecret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
Always verify the signature before processing the event. Reject requests with invalid signatures
with 401 Unauthorized.
Delivery guarantees and retry policy
- AgentIdP delivers each event at least once — your endpoint may receive duplicates
- Use the
idfield to deduplicate events - Delivery is attempted immediately; on failure, retries use exponential backoff
- After repeated failures, the delivery moves to
dead_letterstatus - Subscriptions with high
failure_countmay be automatically disabled
Delivery statuses: pending → delivered (success) or failed (attempt failed) → dead_letter
(all retries exhausted)
List subscriptions
curl -s "http://localhost:3000/api/v1/webhooks" \
-H "Authorization: Bearer $TOKEN" | jq .
Pause or resume a subscription
To pause (disable) a subscription without deleting it:
curl -s -X PATCH "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "active": false }' | jq .
To resume:
curl -s -X PATCH "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "active": true }' | jq .
Inspect delivery history
GET /api/v1/webhooks/{id}/deliveries
curl -s "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID/deliveries?limit=20&offset=0" \
-H "Authorization: Bearer $TOKEN" | jq .
Response:
{
"deliveries": [
{
"id": "del-uuid",
"subscription_id": "wh-uuid",
"event_type": "agent.created",
"payload": { ... },
"status": "delivered",
"http_status_code": 200,
"attempt_count": 1,
"next_retry_at": null,
"delivered_at": "2026-04-04T09:00:01.000Z",
"created_at": "2026-04-04T09:00:00.000Z",
"updated_at": "2026-04-04T09:00:01.000Z"
}
],
"total": 47,
"limit": 20,
"offset": 0
}
Use offset to paginate through delivery history. Increase limit to retrieve more records
per page (the server default is 20).
Delete a subscription
curl -s -X DELETE "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
Expected response: 204. This permanently deletes the subscription and all its delivery records.