Files
SentryAgent.ai Developer 8cabc0191c docs: commit all Phase 6 documentation updates and OpenSpec archives
- 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>
2026-04-07 02:24:24 +00:00

31 KiB
Raw Permalink Blame History

WS4 Spec — guides/

Target files

  • docs/developers/guides/README.md — update index table
  • docs/developers/guides/register-an-agent.md — surgical update
  • docs/developers/guides/manage-credentials.md — surgical update
  • docs/developers/guides/issue-and-revoke-tokens.md — surgical update
  • docs/developers/guides/query-audit-logs.md — surgical update
  • docs/developers/guides/use-analytics-dashboard.md — create new
  • docs/developers/guides/manage-api-tiers.md — create new
  • docs/developers/guides/a2a-delegation.md — create new
  • docs/developers/guides/configure-webhooks.md — create new
  • docs/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 — correct
  • GET /api/v1/agents/{agentId}/credentials — correct
  • POST /api/v1/agents/{agentId}/credentials/{credentialId}/rotate — correct
  • DELETE /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 — correct
  • POST /api/v1/token/introspect — correct
  • POST /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 — correct
  • GET /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 (023, 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 023 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_tier must be pro or enterprise
  • target_tier must 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 the valid field — 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 signingSecret now. 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 id field to deduplicate events
  • Delivery is attempted immediately; on failure, retries use exponential backoff
  • After repeated failures, the delivery moves to dead_letter status
  • Subscriptions with high failure_count may be automatically disabled

Delivery statuses: pendingdelivered (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 window
  • verified: false — the hash chain has a broken link; contact SentryAgent.ai support
  • checkedCount — 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.