- 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>
47 KiB
WS1 Spec — api-reference.md
Target file
docs/developers/api-reference.md
Objective
Replace the current 14-endpoint Phase 1 API reference with a complete reference covering all current endpoints. The Developer must rewrite the file in full. Every endpoint must include: HTTP method + path, one-line description, auth requirement, required scopes, request fields, response schema, error responses, and a working curl example.
Document structure
The updated file must open with the same Base URL and Authentication preamble (no changes). The table of contents must list all sections below. The Errors section must be expanded with all new error codes. Endpoint groups must appear in this order:
- Agent Registry
- Credentials
- OAuth 2.0 / Tokens
- Audit Log
- Organizations
- Analytics
- API Tiers
- Compliance
- Webhooks
- Federation
- DID / OIDC
- A2A Delegation
- Marketplace
Updated preamble (verbatim)
# API Reference
Complete reference for all SentryAgent.ai AgentIdP endpoints.
## Base URL
http://localhost:3000/api/v1
The port is configured via the PORT environment variable (default: 3000).
## Authentication
All endpoints require a JWT Bearer token in the Authorization header unless noted otherwise:
Authorization: Bearer <access_token>
Obtain a token via POST /api/v1/token using your agent's client_id and client_secret.
Endpoints marked **No auth** do not require a Bearer token. Endpoints marked **Unauthenticated** are
intentionally public.
Errors section — updated error codes table
Preserve all existing error codes and add the following new ones:
| Code | HTTP Status | Description |
|---|---|---|
ORG_NOT_FOUND |
404 | Organization with the given orgId does not exist |
ORG_ALREADY_EXISTS |
409 | An organization with this slug already exists |
ORG_SUSPENDED |
403 | Organization is suspended — operations are blocked |
MEMBER_ALREADY_EXISTS |
409 | Agent is already a member of this organization |
DELEGATION_NOT_FOUND |
404 | Delegation chain with the given chainId does not exist |
DELEGATION_EXPIRED |
403 | Delegation token has expired |
DELEGATION_REVOKED |
403 | Delegation token has been revoked |
DELEGATION_SCOPE_EXCEEDED |
403 | Requested scopes exceed the delegator's own scopes |
TIER_UPGRADE_NOT_REQUIRED |
400 | Target tier is not higher than the current tier |
WEBHOOK_NOT_FOUND |
404 | Webhook subscription with the given id does not exist |
PARTNER_NOT_FOUND |
404 | Federation partner with the given id does not exist |
COMPLIANCE_DISABLED |
404 | AGNTCY compliance endpoints are disabled on this instance |
Section 1 — Agent Registry
POST /agents — Register a new agent
Description: Creates a new AI agent identity. The agentId is system-assigned.
Auth: Bearer token with agents:write scope.
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Unique email-format identifier for the agent |
agentType |
enum | Yes | screener | classifier | orchestrator | extractor | summarizer | router | monitor | custom |
version |
string | Yes | Semantic version string, e.g. 1.0.0 |
capabilities |
string[] | Yes | One or more resource:action strings (min 1) |
owner |
string | Yes | Owning team or organisation, 1–128 characters |
deploymentEnv |
enum | Yes | development | staging | production |
organization_id |
string | No | UUID of the org to scope the agent to. Required on multi-tenant instances. |
Response 201 Created:
| Field | Type | Description |
|---|---|---|
agentId |
UUID | System-assigned immutable identifier |
email |
string | Unique email identifier |
agentType |
string | Agent type |
version |
string | Semantic version |
capabilities |
string[] | Capability list |
owner |
string | Owning team |
deploymentEnv |
string | Deployment environment |
status |
string | Always active on creation |
createdAt |
ISO 8601 | Registration timestamp |
updatedAt |
ISO 8601 | Last update timestamp |
Error responses:
| Code | HTTP | Error code |
|---|---|---|
| Validation failure | 400 | VALIDATION_ERROR |
| Invalid token | 401 | UNAUTHORIZED |
| Missing scope | 403 | INSUFFICIENT_SCOPE |
| Free tier limit | 403 | FREE_TIER_LIMIT_EXCEEDED |
| Email taken | 409 | AGENT_ALREADY_EXISTS |
| Rate limit | 429 | RATE_LIMIT_EXCEEDED |
curl example:
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"],
"owner": "talent-team",
"deploymentEnv": "production"
}' | jq .
GET /agents — List agents
Description: Returns a paginated list of registered agents.
Auth: Bearer token with agents:read scope.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number (1-based) |
limit |
integer | 20 | Results per page (max 100) |
owner |
string | — | Filter by owner (exact match) |
agentType |
enum | — | Filter by agent type |
status |
enum | — | Filter by status (active | suspended | decommissioned) |
Response 200 OK: { data: Agent[], total: number, page: number, limit: number }
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/agents?page=1&limit=20&status=active" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /agents/{agentId} — Get agent by ID
Description: Returns the full identity record for a single agent.
Auth: Bearer token with agents:read scope.
Path parameters: agentId (UUID)
Response 200 OK: Full agent object (same fields as POST response).
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 AGENT_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
PATCH /agents/{agentId} — Update agent metadata
Description: Partially updates agent metadata. Immutable fields (agentId, email, createdAt) cannot be changed.
Auth: Bearer token with agents:write scope.
Request body (application/json) — all fields optional:
| Field | Type | Description |
|---|---|---|
agentType |
enum | Updated agent type |
version |
string | Updated semantic version |
capabilities |
string[] | Updated capabilities (replaces full list) |
owner |
string | Updated owner |
deploymentEnv |
enum | Updated deployment environment |
status |
enum | active | suspended | decommissioned. Setting decommissioned is irreversible. |
Response 200 OK: Full updated agent object.
Error responses: 400 VALIDATION_ERROR / IMMUTABLE_FIELD, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE / AGENT_DECOMMISSIONED, 404 AGENT_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X PATCH "http://localhost:3000/api/v1/agents/$AGENT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "version": "1.5.0", "status": "suspended" }' | jq .
DELETE /agents/{agentId} — Decommission an agent
Description: Permanently decommissions an agent (soft delete). All active credentials are immediately revoked. Irreversible.
Auth: Bearer token with agents:write scope.
Response 204 No Content (empty body).
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 AGENT_NOT_FOUND, 409 AGENT_ALREADY_DECOMMISSIONED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X DELETE "http://localhost:3000/api/v1/agents/$AGENT_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
Section 2 — Credentials
POST /agents/{agentId}/credentials — Generate credentials
Description: Creates a new client_id + client_secret pair. The clientSecret is returned once only.
Auth: Bearer token with agents:write scope.
Request body (application/json) — optional:
| Field | Type | Required | Description |
|---|---|---|---|
expiresAt |
ISO 8601 | No | Optional expiry date. Must be a future date. Omit for non-expiring credential. |
Response 201 Created:
| Field | Type | Description |
|---|---|---|
credentialId |
UUID | Unique credential identifier |
clientId |
UUID | Same as agentId |
clientSecret |
string | Plaintext secret (shown once only — store immediately) |
status |
string | active |
createdAt |
ISO 8601 | Creation timestamp |
expiresAt |
ISO 8601 | null | Expiry date or null |
revokedAt |
ISO 8601 | null | Always null on creation |
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE / AGENT_NOT_ACTIVE, 404 AGENT_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "expiresAt": "2027-01-01T00:00:00.000Z" }' | jq .
GET /agents/{agentId}/credentials — List credentials
Description: Returns all credentials for an agent (active and revoked). The clientSecret is never returned.
Auth: Bearer token with agents:read scope.
Query parameters: page (default 1), limit (default 20, max 100), status (active | revoked)
Response 200 OK: { data: Credential[], total: number, page: number, limit: number }
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 AGENT_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/credentials?status=active" \
-H "Authorization: Bearer $TOKEN" | jq .
POST /agents/{agentId}/credentials/{credentialId}/rotate — Rotate a credential
Description: Generates a new clientSecret for the same credentialId. The old secret is immediately invalidated.
Auth: Bearer token with agents:write scope.
Request body (application/json) — optional: { "expiresAt": "ISO 8601" }
Response 200 OK: Full credential object with new clientSecret (shown once only).
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 AGENT_NOT_FOUND / CREDENTIAL_NOT_FOUND, 409 CREDENTIAL_ALREADY_REVOKED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST \
"http://localhost:3000/api/v1/agents/$AGENT_ID/credentials/$CREDENTIAL_ID/rotate" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | jq .
DELETE /agents/{agentId}/credentials/{credentialId} — Revoke a credential
Description: Permanently revokes a credential. Irreversible.
Auth: Bearer token with agents:write scope.
Response 204 No Content.
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 AGENT_NOT_FOUND / CREDENTIAL_NOT_FOUND, 409 CREDENTIAL_ALREADY_REVOKED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X DELETE \
"http://localhost:3000/api/v1/agents/$AGENT_ID/credentials/$CREDENTIAL_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
Section 3 — OAuth 2.0 / Tokens
POST /token — Issue an access token
Description: Issues a signed RS256 JWT via the OAuth 2.0 Client Credentials grant.
Auth: No Bearer token — credentials are in the request body.
Content-Type: application/x-www-form-urlencoded
Request fields (form-encoded):
| Field | Required | Description |
|---|---|---|
grant_type |
Yes | Must be client_credentials |
client_id |
Yes | Agent's agentId (UUID) |
client_secret |
Yes | Credential secret |
scope |
No | Space-separated scopes. If omitted, all scopes are granted. |
Response 200 OK:
| Field | Type | Description |
|---|---|---|
access_token |
string | Signed RS256 JWT |
token_type |
string | Always Bearer |
expires_in |
integer | Lifetime in seconds (3600) |
scope |
string | Granted scopes (space-separated) |
Error responses:
| Code | HTTP | Error |
|---|---|---|
| Bad request / bad grant | 400 | { "error": "unsupported_grant_type" } |
| Bad credentials | 401 | { "error": "invalid_client" } |
| Agent suspended or monthly limit | 403 | { "error": "unauthorized_client" } |
| Rate limit | 429 | RATE_LIMIT_EXCEEDED |
curl example:
curl -s -X POST http://localhost:3000/api/v1/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "scope=agents:read agents:write" | jq .
POST /token/introspect — Introspect a token
Description: Checks whether a token is active. Always returns 200 OK — check the active field.
Auth: Bearer token with tokens:read scope.
Content-Type: application/x-www-form-urlencoded
Request fields: token (required), token_type_hint (optional, access_token)
Response 200 OK (active): { "active": true, "sub": "...", "client_id": "...", "scope": "...", "token_type": "Bearer", "iat": 0, "exp": 0 }
Response 200 OK (inactive): { "active": false }
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/token/introspect \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=$TOKEN_TO_CHECK" | jq .
POST /token/revoke — Revoke a token
Description: Immediately invalidates a token. Idempotent.
Auth: Bearer token.
Content-Type: application/x-www-form-urlencoded
Request fields: token (required), token_type_hint (optional)
Response 200 OK: {} (empty object)
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 FORBIDDEN, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/token/revoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=$TOKEN_TO_REVOKE" | jq .
Section 4 — Audit Log
GET /audit — Query audit log
Description: Returns a paginated, filtered list of audit events (most recent first).
Auth: Bearer token with audit:read scope.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number |
limit |
integer | 50 | Results per page (max 200) |
agentId |
UUID | — | Filter by agent |
action |
string | — | Filter by action type (e.g. token.issued, agent.created) |
outcome |
enum | — | success or failure |
fromDate |
ISO 8601 | — | Events at or after this timestamp (max 90 days ago) |
toDate |
ISO 8601 | — | Events at or before this timestamp |
Response 200 OK: { data: AuditEvent[], total: number, page: number, limit: number }
AuditEvent fields: eventId (UUID), agentId (UUID), action (string), outcome (string), ipAddress (string), userAgent (string), metadata (object), timestamp (ISO 8601)
Error responses: 400 VALIDATION_ERROR / RETENTION_WINDOW_EXCEEDED, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/audit?agentId=$AGENT_ID&action=token.issued&limit=50" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /audit/{eventId} — Get audit event by ID
Description: Returns a single audit event by its immutable eventId.
Auth: Bearer token with audit:read scope.
Path parameters: eventId (UUID)
Response 200 OK: Single AuditEvent object.
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 AUDIT_EVENT_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/audit/$EVENT_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /audit/verify — Verify audit chain integrity
Description: Verifies the cryptographic hash chain of all audit events. Returns verified: true if the chain is intact. Rate limited to 30 req/min (computationally intensive).
Auth: Bearer token with audit:read scope.
Query parameters: fromDate (ISO 8601, optional), toDate (ISO 8601, optional)
Response 200 OK:
| Field | Type | Description |
|---|---|---|
verified |
boolean | true if chain is intact, false if tampering detected |
checkedCount |
integer | Number of events checked |
fromDate |
ISO 8601 | null | Verification window start |
toDate |
ISO 8601 | null | Verification window end |
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/audit/verify" \
-H "Authorization: Bearer $TOKEN" | jq .
Section 5 — Organizations
POST /organizations — Create an organization
Description: Creates a new tenant organization. Agents can be scoped to an organization via organization_id.
Auth: Bearer token (OPA scope enforcement — admin:orgs or equivalent policy).
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Display name, 1–255 characters |
slug |
string | Yes | URL-safe identifier — lowercase letters, digits, hyphens only |
planTier |
enum | No | free (default) | pro | enterprise |
maxAgents |
integer | No | Override the plan default agent limit |
maxTokensPerMonth |
integer | No | Override the plan default monthly token limit |
Response 201 Created:
| Field | Type | Description |
|---|---|---|
organizationId |
UUID | System-assigned organization identifier |
name |
string | Display name |
slug |
string | URL-safe slug |
planTier |
string | Current plan tier |
maxAgents |
integer | Agent limit |
maxTokensPerMonth |
integer | Monthly token limit |
status |
string | active |
createdAt |
ISO 8601 | Creation timestamp |
updatedAt |
ISO 8601 | Last update timestamp |
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 409 ORG_ALREADY_EXISTS, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/organizations \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme AI",
"slug": "acme-ai",
"planTier": "pro"
}' | jq .
GET /organizations — List organizations
Description: Returns a paginated list of organizations. Auth: Bearer token (OPA scope enforcement).
Query parameters: page (default 1), limit (default 20, max 100), status (active | suspended | deleted)
Response 200 OK: { data: Organization[], total: number, page: number, limit: number }
curl example:
curl -s "http://localhost:3000/api/v1/organizations?status=active" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /organizations/{orgId} — Get organization by ID
Description: Returns the full record for a single organization. Auth: Bearer token (OPA scope enforcement).
Path parameters: orgId (UUID)
Response 200 OK: Full organization object.
Error responses: 401 UNAUTHORIZED, 404 ORG_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/organizations/$ORG_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
PATCH /organizations/{orgId} — Update organization
Description: Partially updates an organization. The slug is immutable after creation.
Auth: Bearer token (OPA scope enforcement).
Request body (application/json) — all fields optional:
| Field | Type | Description |
|---|---|---|
name |
string | New display name |
planTier |
enum | free | pro | enterprise |
maxAgents |
integer | New agent limit |
maxTokensPerMonth |
integer | New token limit |
status |
enum | active | suspended (use DELETE to set deleted) |
Response 200 OK: Full updated organization object.
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 404 ORG_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X PATCH "http://localhost:3000/api/v1/organizations/$ORG_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "planTier": "enterprise" }' | jq .
DELETE /organizations/{orgId} — Delete organization
Description: Soft-deletes an organization. Sets status to deleted.
Auth: Bearer token (OPA scope enforcement).
Response 204 No Content.
Error responses: 401 UNAUTHORIZED, 404 ORG_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X DELETE "http://localhost:3000/api/v1/organizations/$ORG_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
POST /organizations/{orgId}/members — Add a member
Description: Adds an existing agent to an organization with a specified role. Auth: Bearer token (OPA scope enforcement).
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
agentId |
UUID | Yes | The agent to add |
role |
enum | Yes | member | admin |
Response 201 Created:
| Field | Type | Description |
|---|---|---|
memberId |
UUID | Membership record identifier |
organizationId |
UUID | Organization |
agentId |
UUID | Agent |
role |
string | member or admin |
joinedAt |
ISO 8601 | Membership creation timestamp |
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 404 ORG_NOT_FOUND / AGENT_NOT_FOUND, 409 MEMBER_ALREADY_EXISTS, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST "http://localhost:3000/api/v1/organizations/$ORG_ID/members" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agentId": "'$AGENT_ID'",
"role": "member"
}' | jq .
Section 6 — Analytics
All analytics endpoints are scoped to the authenticated agent's organization_id.
GET /analytics/tokens — Token issuance trend
Description: Returns daily token issuance counts for the past N days, scoped to the current organization. Auth: Bearer token.
Query parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
days |
integer | 30 | 90 | Number of days to return |
Response 200 OK:
{
"tenantId": "org-uuid",
"days": 30,
"data": [
{ "date": "2026-03-01", "count": 142 },
{ "date": "2026-03-02", "count": 198 }
]
}
Error responses: 400 VALIDATION_ERROR (days > 90), 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/analytics/tokens?days=30" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /analytics/agents/activity — Agent activity heatmap
Description: Returns agent request counts grouped by day-of-week and hour (UTC), for the current organization. Auth: Bearer token.
Response 200 OK:
{
"tenantId": "org-uuid",
"data": [
{ "dow": 1, "hour": 9, "count": 54 },
{ "dow": 1, "hour": 10, "count": 87 }
]
}
dow is 0 (Sunday) through 6 (Saturday). hour is 0–23 UTC.
Error responses: 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/analytics/agents/activity" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /analytics/agents — Per-agent usage summary
Description: Returns token issuance counts per agent for the current calendar month, for the current organization. Auth: Bearer token.
Response 200 OK:
{
"tenantId": "org-uuid",
"month": "2026-03",
"data": [
{ "agentId": "uuid", "tokenCount": 312 },
{ "agentId": "uuid2", "tokenCount": 87 }
]
}
Error responses: 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/analytics/agents" \
-H "Authorization: Bearer $TOKEN" | jq .
Section 7 — API Tiers
GET /tiers/status — Get current tier status
Description: Returns the organization's current plan tier, configured limits, and live usage counters.
Auth: Bearer token with a valid organization_id claim.
Response 200 OK:
| Field | Type | Description |
|---|---|---|
tier |
string | free | pro | enterprise |
limits.maxAgents |
integer | Maximum agents allowed |
limits.maxCallsPerDay |
integer | Maximum API calls per day |
limits.maxTokensPerDay |
integer | Maximum token issuances per day |
usage.agentCount |
integer | Current active agent count |
usage.callsToday |
integer | API calls made today |
usage.tokensToday |
integer | Tokens issued today |
Error responses: 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/tiers/status" \
-H "Authorization: Bearer $TOKEN" | jq .
POST /tiers/upgrade — Initiate tier upgrade
Description: Creates a Stripe Checkout Session to upgrade the organization to a higher plan tier. Returns a one-time checkout URL to redirect the user to.
Auth: Bearer token with a valid organization_id claim.
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
target_tier |
enum | Yes | pro | enterprise — must be higher than current tier |
Response 200 OK:
{ "checkoutUrl": "https://checkout.stripe.com/pay/cs_live_..." }
Error responses: 400 VALIDATION_ERROR / TIER_UPGRADE_NOT_REQUIRED, 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
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 .
Section 8 — Compliance
GET /compliance/controls — SOC 2 control status (public)
Description: Returns the live status of all SOC 2 Trust Services Criteria controls. No authentication required. Auth: None.
Response 200 OK (Cache-Control: public, max-age=60):
{
"controls": [
{ "id": "CC6.1", "name": "Logical Access Controls", "status": "pass", "lastChecked": "2026-04-04T00:00:00.000Z" },
{ "id": "CC7.2", "name": "System Monitoring", "status": "pass", "lastChecked": "2026-04-04T00:00:00.000Z" }
]
}
Each control: id (string), name (string), status (pass | fail | unknown), lastChecked (ISO 8601)
curl example:
curl -s "http://localhost:3000/api/v1/compliance/controls" | jq .
GET /compliance/report — AGNTCY compliance report
Description: Generates an AGNTCY compliance report for the authenticated tenant. Cached in Redis for 5 minutes. Sets X-Cache: HIT when served from cache.
Auth: Bearer token.
Response 200 OK:
{
"tenantId": "org-uuid",
"generatedAt": "2026-04-04T00:00:00.000Z",
"agntcyConformance": true,
"agentCount": 12,
"verifiedAgentCount": 12,
"auditChainIntegrity": true,
"from_cache": false
}
Error responses: 401 UNAUTHORIZED, 404 COMPLIANCE_DISABLED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/compliance/report" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /compliance/agent-cards — Export AGNTCY agent cards
Description: Exports all active agents for the authenticated tenant as AGNTCY-standard agent card JSON objects. Auth: Bearer token.
Response 200 OK: Array of agent card objects.
Each card: did (string), name (string), agentType (string), capabilities (string[]), owner (string), version (string), deploymentEnv (string), identityProvider (string), issuedAt (ISO 8601)
Error responses: 401 UNAUTHORIZED, 404 COMPLIANCE_DISABLED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/compliance/agent-cards" \
-H "Authorization: Bearer $TOKEN" | jq .
Section 9 — Webhooks
All webhook endpoints require Bearer token authentication and are scoped to the authenticated agent's organization_id. Required scopes are enforced via OPA policy.
POST /webhooks — Create a subscription
Description: Creates a new webhook subscription for the organization. The signingSecret is returned once only.
Auth: Bearer token. OPA enforces webhooks:write.
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Human-readable subscription name |
url |
string | Yes | HTTPS endpoint that will receive events |
events |
string[] | Yes | One or more event types to subscribe to (see event type list below) |
Available event types: agent.created, agent.updated, agent.suspended, agent.reactivated, agent.decommissioned, credential.generated, credential.rotated, credential.revoked, token.issued, token.revoked
Response 201 Created:
| Field | Type | Description |
|---|---|---|
id |
UUID | Subscription identifier |
organization_id |
UUID | Owning organization |
name |
string | Subscription name |
url |
string | Target endpoint URL |
events |
string[] | Subscribed event types |
active |
boolean | true |
signingSecret |
string | HMAC-SHA256 signing secret (shown once) |
failure_count |
integer | 0 |
created_at |
ISO 8601 | Creation timestamp |
updated_at |
ISO 8601 | Last update timestamp |
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/webhooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "prod-events",
"url": "https://my-app.example.com/hooks/sentryagent",
"events": ["agent.created", "token.issued"]
}' | jq .
GET /webhooks — List subscriptions
Description: Returns all webhook subscriptions for the organization. Signing secrets are never returned.
Auth: Bearer token. OPA enforces webhooks:read.
Response 200 OK: Array of subscription objects (without signingSecret).
curl example:
curl -s "http://localhost:3000/api/v1/webhooks" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /webhooks/{id} — Get subscription by ID
Description: Returns a single subscription by its UUID.
Auth: Bearer token. OPA enforces webhooks:read.
Path parameters: id (UUID)
Response 200 OK: Single subscription object (without signingSecret).
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 WEBHOOK_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
PATCH /webhooks/{id} — Update subscription
Description: Partially updates a webhook subscription.
Auth: Bearer token. OPA enforces webhooks:write.
Request body (application/json) — all fields optional: name (string), url (string), events (string[]), active (boolean)
Response 200 OK: Updated subscription object.
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 WEBHOOK_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
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 .
DELETE /webhooks/{id} — Delete subscription
Description: Permanently deletes a webhook subscription and all its delivery records.
Auth: Bearer token. OPA enforces webhooks:write.
Response 204 No Content.
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 WEBHOOK_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X DELETE "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
GET /webhooks/{id}/deliveries — List delivery history
Description: Returns a paginated list of delivery attempts for a subscription.
Auth: Bearer token. OPA enforces webhooks:read.
Query parameters: limit (default 20), offset (default 0)
Response 200 OK:
{
"deliveries": [...],
"total": 47,
"limit": 20,
"offset": 0
}
Each delivery: id (UUID), subscription_id (UUID), event_type (string), payload (object), status (pending | delivered | failed | dead_letter), http_status_code (integer | null), attempt_count (integer), next_retry_at (ISO 8601 | null), delivered_at (ISO 8601 | null), created_at (ISO 8601), updated_at (ISO 8601)
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 WEBHOOK_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/webhooks/$WEBHOOK_ID/deliveries?limit=20&offset=0" \
-H "Authorization: Bearer $TOKEN" | jq .
Section 10 — Federation
All partner management endpoints require the admin:orgs scope (enforced via OPA). The verify
endpoint requires any authenticated agent.
POST /federation/trust — Register a trusted partner
Description: Registers a new trusted federation partner (a remote IdP whose tokens this instance will accept).
Auth: Bearer token. OPA enforces admin:orgs.
Request body (application/json): Implementation-defined fields for partner registration including name (string), issuer (string — partner's token issuer URL), jwksUri (string — partner's JWKS endpoint).
Response 201 Created: Partner record with id, name, issuer, jwksUri, createdAt.
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/federation/trust \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "PartnerCorp IdP",
"issuer": "https://idp.partnercorp.com",
"jwksUri": "https://idp.partnercorp.com/.well-known/jwks.json"
}' | jq .
GET /federation/partners — List partners
Description: Returns all registered federation partners.
Auth: Bearer token. OPA enforces admin:orgs.
Response 200 OK: Array of partner records.
curl example:
curl -s "http://localhost:3000/api/v1/federation/partners" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /federation/partners/{id} — Get partner by ID
Description: Returns a single federation partner record.
Auth: Bearer token. OPA enforces admin:orgs.
Path parameters: id (UUID)
Error responses: 401 UNAUTHORIZED, 403 INSUFFICIENT_SCOPE, 404 PARTNER_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s "http://localhost:3000/api/v1/federation/partners/$PARTNER_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
PATCH /federation/partners/{id} — Update partner
Description: Partially updates a federation partner record.
Auth: Bearer token. OPA enforces admin:orgs.
curl example:
curl -s -X PATCH "http://localhost:3000/api/v1/federation/partners/$PARTNER_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Partner Name" }' | jq .
DELETE /federation/partners/{id} — Delete partner
Description: Removes a federation partner. This instance will no longer accept tokens from the partner's issuer.
Auth: Bearer token. OPA enforces admin:orgs.
Response 204 No Content.
curl example:
curl -s -X DELETE "http://localhost:3000/api/v1/federation/partners/$PARTNER_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
POST /federation/verify — Verify a federated token
Description: Verifies a token issued by a trusted federation partner. Returns the decoded claims if the token is valid and the issuer is trusted.
Auth: Bearer token (any authenticated agent — no admin:orgs required).
Request body (application/json): { "token": "<partner-issued-jwt>" }
Response 200 OK: { "valid": true, "claims": { ... } } or { "valid": false, "reason": "..." }
curl example:
curl -s -X POST http://localhost:3000/api/v1/federation/verify \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "token": "'$PARTNER_TOKEN'" }' | jq .
Section 11 — DID / OIDC
GET /agents/{agentId}/did — Get agent DID document
Description: Returns the W3C DID Core 1.0 document for an agent. Unauthenticated — publicly accessible. Auth: None.
Response 200 OK: W3C DID Document.
{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:web:localhost%3A3000:agents:a1b2c3d4",
"controller": "did:web:localhost%3A3000:agents:a1b2c3d4",
"verificationMethod": [{
"id": "did:web:localhost%3A3000:agents:a1b2c3d4#key-1",
"type": "JsonWebKey2020",
"controller": "did:web:localhost%3A3000:agents:a1b2c3d4",
"publicKeyJwk": { "kty": "RSA", "n": "...", "e": "AQAB" }
}],
"authentication": ["did:web:localhost%3A3000:agents:a1b2c3d4#key-1"],
"agntcy": {
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agentType": "screener",
"capabilities": ["resume:read"],
"deploymentEnv": "production",
"owner": "talent-team",
"version": "1.0.0"
}
}
curl example:
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/did" | jq .
GET /agents/{agentId}/did/resolve — Resolve agent DID
Description: Returns the full W3C DID Resolution Result format including metadata. Auth: Bearer token + OPA policy.
Response 200 OK:
{
"didDocument": { ... },
"didDocumentMetadata": {
"created": "2026-03-28T09:00:00.000Z",
"updated": "2026-03-28T09:00:00.000Z",
"deactivated": false
},
"didResolutionMetadata": {
"contentType": "application/did+ld+json",
"retrieved": "2026-04-04T00:00:00.000Z"
}
}
curl example:
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/did/resolve" \
-H "Authorization: Bearer $TOKEN" | jq .
GET /agents/{agentId}/did/card — Get AGNTCY agent card
Description: Returns the AGNTCY-format agent card for an agent. Unauthenticated. Auth: None.
Response 200 OK:
{
"did": "did:web:localhost%3A3000:agents:a1b2c3d4",
"name": "screener-001@talent.ai",
"agentType": "screener",
"capabilities": ["resume:read"],
"owner": "talent-team",
"version": "1.0.0",
"deploymentEnv": "production",
"identityProvider": "https://sentryagent.ai",
"issuedAt": "2026-04-04T00:00:00.000Z"
}
curl example:
curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID/did/card" | jq .
GET /.well-known/openid-configuration — OIDC discovery document
Description: Returns the OIDC Provider discovery document. Unauthenticated. Mounted at the server root (not under /api/v1).
Auth: None.
curl example:
curl -s "http://localhost:3000/.well-known/openid-configuration" | jq .
GET /.well-known/jwks.json — JWKS endpoint
Description: Returns the JSON Web Key Set (public keys used to verify ID tokens). Unauthenticated. Auth: None.
curl example:
curl -s "http://localhost:3000/.well-known/jwks.json" | jq .
GET /agent-info — Agent identity claims
Description: Returns identity claims for the authenticated agent (equivalent to UserInfo in OIDC). Mounted at the server root. Auth: Bearer token.
curl example:
curl -s "http://localhost:3000/agent-info" \
-H "Authorization: Bearer $TOKEN" | jq .
POST /api/v1/oidc/token — OIDC token exchange (GitHub Actions)
Description: Exchanges a GitHub OIDC JWT for a SentryAgent.ai access token. Unauthenticated — the GitHub OIDC token is the credential. Trust-policy enforcement happens inside the controller. Auth: None (GitHub OIDC JWT in body).
Request body (application/json): { "github_token": "<github-oidc-jwt>", "agentId": "<uuid>" }
Response 200 OK: { "access_token": "...", "token_type": "Bearer", "expires_in": 3600 }
curl example:
curl -s -X POST http://localhost:3000/api/v1/oidc/token \
-H "Content-Type: application/json" \
-d '{
"github_token": "'$GITHUB_OIDC_TOKEN'",
"agentId": "'$AGENT_ID'"
}' | jq .
POST /api/v1/oidc/trust-policies — Create trust policy
Description: Registers a trust policy that allows GitHub Actions workflows matching specific claims to exchange tokens.
Auth: Bearer token with agents:write scope.
Request body (application/json): Repository, branch, and claim constraints (implementation-defined fields).
curl example:
curl -s -X POST http://localhost:3000/api/v1/oidc/trust-policies \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agentId": "'$AGENT_ID'",
"repository": "my-org/my-repo",
"branch": "main"
}' | jq .
GET /api/v1/oidc/trust-policies — List trust policies
Description: Returns all trust policies for an agent.
Auth: Bearer token with agents:write scope.
Query parameters: agentId (UUID, required)
curl example:
curl -s "http://localhost:3000/api/v1/oidc/trust-policies?agentId=$AGENT_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
DELETE /api/v1/oidc/trust-policies/{id} — Delete trust policy
Description: Deletes a trust policy by its UUID.
Auth: Bearer token with agents:write scope.
Response 204 No Content.
curl example:
curl -s -X DELETE "http://localhost:3000/api/v1/oidc/trust-policies/$POLICY_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
Section 12 — A2A Delegation
POST /oauth2/token/delegate — Create a delegation chain
Description: Creates a delegation chain that grants a delegatee agent a subset of the delegator's scopes for a limited time. Auth: Bearer token.
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
delegateeAgentId |
UUID | Yes | The agent that receives delegated authority |
scopes |
string[] | Yes | Scopes to delegate — must be a strict subset of the caller's own scopes |
ttlSeconds |
integer | Yes | Delegation lifetime in seconds. Min: 60, Max: 86400 |
Response 201 Created:
| Field | Type | Description |
|---|---|---|
delegationToken |
string | Signed delegation token (HMAC-SHA256) |
chainId |
UUID | Delegation chain identifier |
delegatorAgentId |
UUID | Agent granting the delegation |
delegateeAgentId |
UUID | Agent receiving the delegation |
scopes |
string[] | Delegated scopes |
expiresAt |
ISO 8601 | Expiry timestamp |
Error responses: 400 VALIDATION_ERROR / DELEGATION_SCOPE_EXCEEDED, 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/oauth2/token/delegate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"delegateeAgentId": "'$DELEGATEE_AGENT_ID'",
"scopes": ["agents:read"],
"ttlSeconds": 3600
}' | jq .
POST /oauth2/token/verify-delegation — Verify a delegation token
Description: Verifies a delegation token and returns the chain details. Returns valid: false (not an error) for expired or revoked tokens.
Auth: Bearer token.
Request body (application/json): { "delegationToken": "<delegation-jwt>" }
Response 200 OK:
| Field | Type | Description |
|---|---|---|
valid |
boolean | Whether the delegation is currently valid |
chainId |
UUID | Chain identifier |
delegatorAgentId |
UUID | Delegating agent |
delegateeAgentId |
UUID | Receiving agent |
scopes |
string[] | Delegated scopes |
issuedAt |
ISO 8601 | Issue timestamp |
expiresAt |
ISO 8601 | Expiry timestamp |
revokedAt |
ISO 8601 | null | Revocation timestamp, or null |
Error responses: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X POST http://localhost:3000/api/v1/oauth2/token/verify-delegation \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "delegationToken": "'$DELEGATION_TOKEN'" }' | jq .
DELETE /oauth2/token/delegate/{chainId} — Revoke a delegation chain
Description: Immediately revokes a delegation chain. The delegatee can no longer use the delegation token. Only the delegator can revoke their own chains. Auth: Bearer token.
Path parameters: chainId (UUID)
Response 204 No Content.
Error responses: 401 UNAUTHORIZED, 403 FORBIDDEN, 404 DELEGATION_NOT_FOUND, 429 RATE_LIMIT_EXCEEDED
curl example:
curl -s -X DELETE "http://localhost:3000/api/v1/oauth2/token/delegate/$CHAIN_ID" \
-H "Authorization: Bearer $TOKEN" \
-o /dev/null -w "%{http_code}\n"
Section 13 — Marketplace
The Marketplace is feature-flagged via MARKETPLACE_ENABLED env var. When disabled, all endpoints
return 404 NOT_FOUND. All marketplace endpoints are unauthenticated — no Bearer token required.
GET /marketplace/agents — List public agents
Description: Returns a paginated list of publicly-listed agents. Auth: None.
Query parameters: page (default 1), limit (default 20, max 100), q (text search), capability (filter by capability string), publisher (filter by owner)
Response 200 OK: { data: PublicAgent[], total: number, page: number, limit: number }
curl example:
curl -s "http://localhost:3000/api/v1/marketplace/agents?q=screener&limit=20" | jq .
GET /marketplace/agents/{agentId} — Get public agent
Description: Returns a single public agent with its DID document included. Returns 404 if the agent is private or inactive. Auth: None.
Path parameters: agentId (UUID)
Response 200 OK: Public agent object including didDocument field.
Error responses: 404 AGENT_NOT_FOUND
curl example:
curl -s "http://localhost:3000/api/v1/marketplace/agents/$AGENT_ID" | jq .