- 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>
31 KiB
WS4 Spec — guides/
Target files
docs/developers/guides/README.md— update index tabledocs/developers/guides/register-an-agent.md— surgical updatedocs/developers/guides/manage-credentials.md— surgical updatedocs/developers/guides/issue-and-revoke-tokens.md— surgical updatedocs/developers/guides/query-audit-logs.md— surgical updatedocs/developers/guides/use-analytics-dashboard.md— create newdocs/developers/guides/manage-api-tiers.md— create newdocs/developers/guides/a2a-delegation.md— create newdocs/developers/guides/configure-webhooks.md— create newdocs/developers/guides/agntcy-compliance.md— create new
Part A — Updates to existing guides
A1. guides/README.md
Find (entire table):
| Guide | What it covers |
|-------|----------------|
| [Register an Agent](register-an-agent.md) | All registration fields, validation rules, common errors and fixes |
| [Manage Credentials](manage-credentials.md) | Generate, list, rotate, and revoke credentials |
| [Issue and Revoke Tokens](issue-and-revoke-tokens.md) | OAuth 2.0 Client Credentials flow, JWT structure, introspect, revoke |
| [Query Audit Logs](query-audit-logs.md) | Filters, pagination, event structure, 90-day retention |
Replace with (adding 5 new rows):
| Guide | What it covers |
|-------|----------------|
| [Register an Agent](register-an-agent.md) | All registration fields, organization scoping, validation rules, common errors |
| [Manage Credentials](manage-credentials.md) | Generate, list, rotate, and revoke credentials |
| [Issue and Revoke Tokens](issue-and-revoke-tokens.md) | OAuth 2.0 Client Credentials flow, JWT structure, introspect, revoke |
| [Query Audit Logs](query-audit-logs.md) | Filters, pagination, event structure, 90-day retention |
| [Use the Analytics Dashboard](use-analytics-dashboard.md) | Query token trends, agent activity heatmap, and per-agent usage |
| [Manage API Tiers](manage-api-tiers.md) | Check current tier, understand limits, trigger a Stripe upgrade |
| [A2A Delegation](a2a-delegation.md) | Create and verify agent-to-agent delegation chains |
| [Configure Webhooks](configure-webhooks.md) | Subscribe to events, understand delivery guarantees, inspect history |
| [AGNTCY Compliance](agntcy-compliance.md) | Export agent cards, generate compliance reports, verify audit chain |
A2. guides/register-an-agent.md
Edit A2a — Add organization_id to request fields table
Find (last row of the request fields table):
| `deploymentEnv` | string (enum) | Yes | Target deployment environment. See values below. |
Replace with:
| `deploymentEnv` | string (enum) | Yes | Target deployment environment. See values below. |
| `organization_id` | string (UUID) | No | UUID of the organization to scope this agent to. Recommended on all multi-tenant instances. |
Edit A2b — Update the example curl command to include organization_id
Find (the curl command in the example section):
curl -s -X POST http://localhost:3000/api/v1/agents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "screener-001@talent.ai",
"agentType": "screener",
"version": "1.0.0",
"capabilities": ["resume:read", "email:send", "candidate:score"],
"owner": "talent-acquisition-team",
"deploymentEnv": "production"
}' | jq .
Replace with:
curl -s -X POST http://localhost:3000/api/v1/agents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "screener-001@talent.ai",
"agentType": "screener",
"version": "1.0.0",
"capabilities": ["resume:read", "email:send", "candidate:score"],
"owner": "talent-acquisition-team",
"deploymentEnv": "production",
"organization_id": "'$ORG_ID'"
}' | jq .
Edit A2c — Add note about organization_id after the curl example
After the "The agentId is assigned by the system — it is immutable and never changes." line,
insert:
> **Organization scoping**: If you include `organization_id` in the request, the agent is
> associated with that organization. Analytics, webhook events, and tier enforcement are all
> scoped by organization. To create an organization first, see the
> [Quick Start](../quick-start.md) guide.
A3. guides/manage-credentials.md
No content changes needed to this guide. All endpoint paths are current:
POST /api/v1/agents/{agentId}/credentials— correctGET /api/v1/agents/{agentId}/credentials— correctPOST /api/v1/agents/{agentId}/credentials/{credentialId}/rotate— correctDELETE /api/v1/agents/{agentId}/credentials/{credentialId}— correct
Edit A3a — Add a note about org-scoped token issuance at the top
After the first paragraph ("A credential is a client_id + client_secret pair..."), insert:
> **Multi-tenant note**: Credentials issued for an agent that belongs to an organization will
> produce tokens carrying an `organization_id` claim. This claim is required by analytics,
> webhooks, tier enforcement, and A2A delegation. Ensure your agent is registered with
> `organization_id` before issuing credentials for production use.
A4. guides/issue-and-revoke-tokens.md
No endpoint path changes needed. Paths are current:
POST /api/v1/token— correctPOST /api/v1/token/introspect— correctPOST /api/v1/token/revoke— correct
Edit A4a — Expand available scopes table
Find (the scopes table):
| Scope | What it allows |
|-------|----------------|
| `agents:read` | Read agent records |
| `agents:write` | Create, update, decommission agents |
| `tokens:read` | Introspect tokens |
| `audit:read` | Query audit logs |
Replace with (add new scopes for Phase 6 features):
| Scope | What it allows |
|-------|----------------|
| `agents:read` | Read agent identity records |
| `agents:write` | Create, update, and decommission agents |
| `tokens:read` | Introspect tokens |
| `audit:read` | Query audit logs and verify audit chain integrity |
| `webhooks:read` | List webhook subscriptions and delivery history |
| `webhooks:write` | Create, update, and delete webhook subscriptions |
| `admin:orgs` | Manage organizations and federation partners |
A5. guides/query-audit-logs.md
No endpoint path changes needed. Paths are current:
GET /api/v1/audit— correctGET /api/v1/audit/{eventId}— correct
Edit A5a — Add audit/verify to the action table
After the existing action table (the "What gets logged" section), add a note:
> **Audit chain verification**: In addition to querying events, you can verify the cryptographic
> integrity of the entire audit hash chain via `GET /api/v1/audit/verify`. This endpoint requires
> `audit:read` scope and is rate-limited to 30 requests/min. See the
> [API Reference](../api-reference.md#get-auditverify---verify-audit-chain-integrity) for details.
Part B — New guide files
B1. guides/use-analytics-dashboard.md
Write a complete new file with this content:
# Use the Analytics Dashboard
This guide explains how to query the three analytics endpoints to understand your organization's
token usage and agent activity patterns.
All analytics endpoints require Bearer token authentication and are scoped to the organization
embedded in your token.
---
## Prerequisites
- A running AgentIdP instance
- A valid Bearer token with `organization_id` in its claims
- At least one agent registered and some token issuance activity
---
## Token issuance trend
`GET /api/v1/analytics/tokens`
Returns daily token issuance counts for the past N days (default 30, max 90). Use this to
track usage growth, identify traffic spikes, and plan capacity.
```bash
curl -s "http://localhost:3000/api/v1/analytics/tokens?days=30" \
-H "Authorization: Bearer $TOKEN" | jq .
Response:
{
"tenantId": "org-0a1b2c3d-e4f5-6789-abcd-ef0123456789",
"days": 30,
"data": [
{ "date": "2026-03-06", "count": 142 },
{ "date": "2026-03-07", "count": 198 },
{ "date": "2026-03-08", "count": 0 }
]
}
Interpreting the data: Each item in data is one calendar day (UTC) with the number of
tokens issued on that day. Days with zero issuance are included with count: 0. The array
is ordered chronologically, oldest first.
Using it: Compare day-over-day counts to identify growth or anomalies. A sudden spike in
count may indicate an agent retry loop or a credential leak. Zero-count days during expected
operation may indicate a deployment issue.
Query parameter: days — positive integer, max 90. Returns 400 VALIDATION_ERROR if
exceeded.
# Last 7 days
curl -s "http://localhost:3000/api/v1/analytics/tokens?days=7" \
-H "Authorization: Bearer $TOKEN" | jq .
# Last 90 days (maximum)
curl -s "http://localhost:3000/api/v1/analytics/tokens?days=90" \
-H "Authorization: Bearer $TOKEN" | jq .
Agent activity heatmap
GET /api/v1/analytics/agents/activity
Returns request counts grouped by day-of-week (0 = Sunday, 6 = Saturday) and hour (0–23, UTC). Use this to identify peak usage windows for capacity planning and rate limit tuning.
curl -s "http://localhost:3000/api/v1/analytics/agents/activity" \
-H "Authorization: Bearer $TOKEN" | jq .
Response:
{
"tenantId": "org-0a1b2c3d-e4f5-6789-abcd-ef0123456789",
"data": [
{ "dow": 1, "hour": 9, "count": 54 },
{ "dow": 1, "hour": 10, "count": 87 },
{ "dow": 3, "hour": 14, "count": 201 }
]
}
Interpreting the data: dow is 0 (Sunday) through 6 (Saturday). hour is 0–23 UTC.
Only non-zero cells are returned — missing combinations had zero activity. Sort by count
descending to find your peak windows.
Using it: If most activity is on weekday mornings UTC, ensure your rate limit headroom covers that window. If weekend activity is unexpectedly high, investigate which agents are active.
Per-agent usage summary
GET /api/v1/analytics/agents
Returns token issuance counts per agent for the current calendar month (UTC). Use this to identify your most active agents and check if any single agent is consuming a disproportionate share of your monthly token budget.
curl -s "http://localhost:3000/api/v1/analytics/agents" \
-H "Authorization: Bearer $TOKEN" | jq .
Response:
{
"tenantId": "org-0a1b2c3d-e4f5-6789-abcd-ef0123456789",
"month": "2026-04",
"data": [
{ "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "tokenCount": 312 },
{ "agentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "tokenCount": 87 }
]
}
Interpreting the data: Each item shows an agent UUID and the number of tokens it has issued this month. The response covers the full current calendar month from day 1 to now. It resets on the first day of each month.
Using it: Cross-reference agentId values against GET /api/v1/agents to identify which
agents by name. If one agent accounts for >80% of usage, investigate whether it is token
caching correctly or requesting tokens unnecessarily.
---
### B2. guides/manage-api-tiers.md
Write a complete new file with this content:
```markdown
# Manage API Tiers
This guide explains how to check your organization's current plan tier, understand the enforced
limits, and initiate an upgrade via Stripe.
---
## Prerequisites
- A running AgentIdP instance
- A valid Bearer token with `organization_id` in its claims
---
## Check current tier status
`GET /api/v1/tiers/status`
Returns your organization's tier, the configured limits, and live usage counters for today.
```bash
curl -s "http://localhost:3000/api/v1/tiers/status" \
-H "Authorization: Bearer $TOKEN" | jq .
Response:
{
"tier": "free",
"limits": {
"maxAgents": 10,
"maxCallsPerDay": 1000,
"maxTokensPerDay": 1000
},
"usage": {
"agentCount": 3,
"callsToday": 142,
"tokensToday": 87
}
}
Understanding the fields:
| Field | Description |
|---|---|
tier |
Current plan: free, pro, or enterprise |
limits.maxAgents |
Maximum active (non-decommissioned) agents allowed |
limits.maxCallsPerDay |
Maximum total API calls per calendar day (UTC) |
limits.maxTokensPerDay |
Maximum token issuances per calendar day (UTC) |
usage.agentCount |
Current number of active agents |
usage.callsToday |
API calls made so far today |
usage.tokensToday |
Tokens issued so far today |
When limits are reached: The relevant endpoint returns 403 FREE_TIER_LIMIT_EXCEEDED.
Daily counters reset at midnight UTC. The agent count limit is a current count, not a daily
counter — decommissioning an agent immediately frees capacity.
Tier comparison
| Limit | Free | Pro | Enterprise |
|---|---|---|---|
| Max agents | 10 | 100 | Unlimited |
| Max API calls / day | 1,000 | 50,000 | Unlimited |
| Max token issuances / day | 1,000 | 50,000 | Unlimited |
Upgrade your tier
POST /api/v1/tiers/upgrade
Creates a Stripe Checkout Session and returns a one-time URL. Complete the payment in the browser to upgrade your organization's tier.
curl -s -X POST http://localhost:3000/api/v1/tiers/upgrade \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "target_tier": "pro" }' | jq .
Response:
{
"checkoutUrl": "https://checkout.stripe.com/pay/cs_live_a1b2c3d4e5f6..."
}
Open checkoutUrl in a browser to complete payment. After successful payment, Stripe sends a
webhook to AgentIdP which automatically upgrades your organization's tier.
Constraints:
target_tiermust beproorenterprisetarget_tiermust be higher than your current tier (you cannot downgrade via this endpoint)- Attempting to upgrade to the current or a lower tier returns
400 VALIDATION_ERROR
# Upgrade from free to pro
curl -s -X POST http://localhost:3000/api/v1/tiers/upgrade \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "target_tier": "pro" }' | jq .
# Upgrade from pro to enterprise
curl -s -X POST http://localhost:3000/api/v1/tiers/upgrade \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "target_tier": "enterprise" }' | jq .
Common errors
400 VALIDATION_ERROR — target_tier missing or invalid
{
"code": "VALIDATION_ERROR",
"message": "target_tier must be one of: free, pro, enterprise.",
"details": { "received": "premium" }
}
Fix: Use "pro" or "enterprise".
400 TIER_UPGRADE_NOT_REQUIRED — not an upgrade
Fix: You are already on this tier or a higher tier. Check GET /api/v1/tiers/status first.
401 UNAUTHORIZED — token lacks organization_id
The tier endpoints require a token with an organization_id claim. Use a token issued by an
agent that was registered with organization_id. Tokens issued via the bootstrap method
(without an org) do not carry organization_id and will fail.
---
### B3. guides/a2a-delegation.md
Write a complete new file with this content:
```markdown
# A2A Delegation
Agent-to-Agent (A2A) delegation lets one agent grant another agent a subset of its OAuth 2.0
scopes for a defined period. This is the foundation for building secure multi-agent pipelines
where an orchestrator agent coordinates specialist sub-agents.
---
## Prerequisites
- A running AgentIdP instance
- Two registered agents: the delegator (has a Bearer token) and the delegatee (knows its
`agentId`)
- The delegator's scopes must be a superset of the scopes it wants to delegate
---
## How delegation works
Delegator agent Delegatee agent | | |-- POST /oauth2/token/delegate ----------->| (creates chain server-side) |<-- { delegationToken, chainId, scopes } --| | | |-- passes delegationToken out-of-band ---->| | | | POST /oauth2/token/verify-delegation | <-- { valid: true, scopes, expiresAt } | | | (optional) DELETE /oauth2/token/delegate/{chainId}
---
## Step 1 — Create a delegation chain
The delegator agent creates the chain by specifying the delegatee's `agentId`, the scopes to
delegate (must be a strict subset of the delegator's own scopes), and the TTL in seconds.
```bash
curl -s -X POST http://localhost:3000/api/v1/oauth2/token/delegate \
-H "Authorization: Bearer $DELEGATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"delegateeAgentId": "'$DELEGATEE_AGENT_ID'",
"scopes": ["agents:read"],
"ttlSeconds": 3600
}' | jq .
Response (201 Created):
{
"delegationToken": "sa_del_a1b2c3d4e5f6...",
"chainId": "d4e5f6a7-b8c9-0123-def0-123456789abc",
"delegatorAgentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"delegateeAgentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"scopes": ["agents:read"],
"expiresAt": "2026-04-04T10:00:00.000Z"
}
Save the delegationToken and chainId:
export DELEGATION_TOKEN="sa_del_a1b2c3d4e5f6..."
export CHAIN_ID="d4e5f6a7-b8c9-0123-def0-123456789abc"
TTL constraints: minimum 60 seconds, maximum 86400 seconds (24 hours). Choose the minimum TTL that covers the delegatee's task.
Step 2 — Pass the delegation token to the delegatee
Pass DELEGATION_TOKEN to the delegatee agent out-of-band. This can be via a shared queue,
a direct API call to the sub-agent, or any other channel. The token is a signed opaque string —
do not parse it; treat it as an opaque credential.
Step 3 — Verify the delegation token
The delegatee (or any agent checking the delegation) calls the verify endpoint. This confirms the chain is valid and not expired or revoked.
curl -s -X POST http://localhost:3000/api/v1/oauth2/token/verify-delegation \
-H "Authorization: Bearer $DELEGATEE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "delegationToken": "'$DELEGATION_TOKEN'" }' | jq .
Response (200 OK — valid delegation):
{
"valid": true,
"chainId": "d4e5f6a7-b8c9-0123-def0-123456789abc",
"delegatorAgentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"delegateeAgentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"scopes": ["agents:read"],
"issuedAt": "2026-04-04T09:00:00.000Z",
"expiresAt": "2026-04-04T10:00:00.000Z",
"revokedAt": null
}
Response (200 OK — expired delegation):
{
"valid": false,
"chainId": "d4e5f6a7-b8c9-0123-def0-123456789abc",
"delegatorAgentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"delegateeAgentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"scopes": ["agents:read"],
"issuedAt": "2026-04-03T09:00:00.000Z",
"expiresAt": "2026-04-03T10:00:00.000Z",
"revokedAt": null
}
The verify endpoint always returns
200 OK. Check thevalidfield — it is never an error response for an expired or revoked token.
Step 4 — (Optional) Revoke the delegation early
If the delegatee has completed its task and you want to revoke the delegation before it expires, the delegator calls:
curl -s -X DELETE "http://localhost:3000/api/v1/oauth2/token/delegate/$CHAIN_ID" \
-H "Authorization: Bearer $DELEGATOR_TOKEN" \
-o /dev/null -w "%{http_code}\n"
Expected response: 204 (no body).
After revocation, verify requests for this chain return { "valid": false, "revokedAt": "<timestamp>" }.
Scope rules
- Delegated scopes must be a strict subset of the delegator's own token scopes
- You cannot delegate scopes you do not have
- You cannot delegate to yourself (delegateeAgentId must differ from delegatorAgentId)
- Delegation is not transitive — a delegatee cannot re-delegate to a third agent
Common errors
400 VALIDATION_ERROR — scope not a subset
The delegator attempted to delegate a scope it does not hold. Check GET /api/v1/token/introspect
to confirm which scopes your token carries.
400 VALIDATION_ERROR — ttlSeconds out of range
Min: 60, Max: 86400. Values outside this range return a validation error.
---
### B4. guides/configure-webhooks.md
Write a complete new file with this content:
```markdown
# 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_id` in its claims
- A publicly reachable HTTPS endpoint to receive events (for local development, use a tool
like [ngrok](https://ngrok.com))
---
## 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`
```bash
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.
---
### B5. guides/agntcy-compliance.md
Write a complete new file with this content:
```markdown
# AGNTCY Compliance
This guide explains how to use AgentIdP's AGNTCY compliance features: exporting agent cards,
generating compliance reports, verifying audit chain integrity, and checking SOC 2 control status.
---
## Prerequisites
- A running AgentIdP instance
- `COMPLIANCE_ENABLED` environment variable not set to `false` (enabled by default)
- A valid Bearer token (for authenticated endpoints)
- At least one registered agent
---
## What is AGNTCY?
AGNTCY is an open standard from the Linux Foundation for AI agent identity and governance.
AgentIdP implements AGNTCY by giving every agent a DID and an agent card. The compliance
endpoints let you export and report on that data in structured, auditable formats.
---
## Export agent cards
`GET /api/v1/compliance/agent-cards`
Exports all active agents in your organization as AGNTCY-standard agent card JSON objects.
Suitable for ingestion by external compliance tools or AGNTCY-compatible registries.
```bash
curl -s "http://localhost:3000/api/v1/compliance/agent-cards" \
-H "Authorization: Bearer $TOKEN" | jq .
Response (200 OK): Array of agent card objects.
[
{
"did": "did:web:localhost%3A3000:agents:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "screener-001@talent.ai",
"agentType": "screener",
"capabilities": ["resume:read", "email:send"],
"owner": "talent-team",
"version": "1.0.0",
"deploymentEnv": "production",
"identityProvider": "https://sentryagent.ai",
"issuedAt": "2026-04-04T09:00:00.000Z"
}
]
Use cases:
- Share with external auditors to demonstrate your agent fleet
- Import into AGNTCY-compatible discovery registries
- Baseline snapshot before and after deployments
Save the output to a file:
curl -s "http://localhost:3000/api/v1/compliance/agent-cards" \
-H "Authorization: Bearer $TOKEN" > agent-cards-$(date +%Y%m%d).json
Generate a compliance report
GET /api/v1/compliance/report
Generates an AGNTCY compliance report for your tenant. The report is cached for 5 minutes
(check the X-Cache header to see if the response is fresh or cached).
curl -s "http://localhost:3000/api/v1/compliance/report" \
-H "Authorization: Bearer $TOKEN" | jq .
Response (200 OK):
{
"tenantId": "org-0a1b2c3d-e4f5-6789-abcd-ef0123456789",
"generatedAt": "2026-04-04T09:00:00.000Z",
"agntcyConformance": true,
"agentCount": 12,
"verifiedAgentCount": 12,
"auditChainIntegrity": true,
"from_cache": false
}
Interpreting the fields:
| Field | Description |
|---|---|
agntcyConformance |
true if all agents have valid DIDs and the audit chain is intact |
agentCount |
Total active agents in the organization |
verifiedAgentCount |
Agents with a resolvable DID document |
auditChainIntegrity |
true if the audit event hash chain has not been tampered with |
from_cache |
true if served from Redis cache (up to 5 minutes old) |
Force a fresh report: Wait 5 minutes for the cache to expire. The from_cache: false
response is always freshly generated.
Verify audit chain integrity
GET /api/v1/audit/verify
Verifies that the cryptographic hash chain of audit events is intact. Returns verified: true
if no tampering is detected. Rate limited to 30 requests/minute (computationally intensive).
Requires: Bearer token with audit:read scope.
curl -s "http://localhost:3000/api/v1/audit/verify" \
-H "Authorization: Bearer $TOKEN" | jq .
Response (200 OK):
{
"verified": true,
"checkedCount": 1247,
"fromDate": null,
"toDate": null
}
Verify a specific date window:
curl -s "http://localhost:3000/api/v1/audit/verify?fromDate=2026-03-01T00:00:00.000Z&toDate=2026-03-31T23:59:59.999Z" \
-H "Authorization: Bearer $TOKEN" | jq .
Interpreting the result:
verified: true— no tampering detected in the checked windowverified: false— the hash chain has a broken link; contact SentryAgent.ai supportcheckedCount— number of audit events verified
Check SOC 2 control status (public)
GET /api/v1/compliance/controls
Returns the live status of all SOC 2 Trust Services Criteria controls. No authentication
required. Responses are cached by CDN/proxies for 60 seconds (Cache-Control: public, max-age=60).
curl -s "http://localhost:3000/api/v1/compliance/controls" | jq .
Response (200 OK):
{
"controls": [
{
"id": "CC6.1",
"name": "Logical Access Controls",
"status": "pass",
"lastChecked": "2026-04-04T08:00:00.000Z"
},
{
"id": "CC7.2",
"name": "System Monitoring",
"status": "pass",
"lastChecked": "2026-04-04T08:00:00.000Z"
}
]
}
Each control has a status of pass, fail, or unknown. Status is updated by background
jobs that run periodically. This endpoint is suitable for embedding in external status pages
or compliance dashboards without sharing API credentials.
When compliance endpoints are disabled
If COMPLIANCE_ENABLED=false is set in the server environment, the AGNTCY compliance endpoints
(/compliance/report and /compliance/agent-cards) return 404 COMPLIANCE_DISABLED. The SOC 2
endpoints (/compliance/controls and /audit/verify) are never gated and always active.