- 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>
48 KiB
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.
Table of Contents
- Errors
- Agent Registry
- Credentials
- OAuth 2.0 / Tokens
- Audit Log
- Organizations
- Analytics
- API Tiers
- Compliance
- Webhooks
- Federation
- DID / OIDC
- A2A Delegation
- Marketplace
Errors
All error responses use this envelope:
{
"code": "ERROR_CODE",
"message": "Human-readable description.",
"details": {}
}
The details field is optional and provides additional context (e.g. which field failed validation).
Error codes
| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR |
400 | Request body or query parameter failed validation |
UNAUTHORIZED |
401 | Missing, expired, or invalid Bearer token |
FORBIDDEN |
403 | Valid token but insufficient scope |
AGENT_NOT_FOUND |
404 | Agent with the given agentId does not exist |
CREDENTIAL_NOT_FOUND |
404 | Credential with the given credentialId does not exist |
AUDIT_EVENT_NOT_FOUND |
404 | Audit event with the given eventId does not exist (or outside retention window) |
AGENT_ALREADY_EXISTS |
409 | An agent with this email is already registered |
AGENT_ALREADY_DECOMMISSIONED |
409 | Agent has already been decommissioned |
CREDENTIAL_ALREADY_REVOKED |
409 | Credential has already been revoked |
RATE_LIMIT_EXCEEDED |
429 | 100 req/min limit exceeded |
FREE_TIER_LIMIT_EXCEEDED |
403 | Free tier resource limit reached |
INSUFFICIENT_SCOPE |
403 | Token is missing a required scope |
IMMUTABLE_FIELD |
400 | Attempt to modify a field that cannot be changed |
AGENT_NOT_ACTIVE |
403 | Operation requires agent to be in active status |
AGENT_DECOMMISSIONED |
403 | Cannot modify a decommissioned agent |
RETENTION_WINDOW_EXCEEDED |
400 | Requested audit date is outside the 90-day retention window |
INTERNAL_SERVER_ERROR |
500 | Unexpected server error |
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 |
Rate limit headers
Every response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per minute (100) |
X-RateLimit-Remaining |
Requests remaining in current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
On 429 responses, wait until X-RateLimit-Reset before retrying.
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 .