Files
sentryagent-idp/docs/developers/api-reference.md
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

48 KiB
Raw Blame History

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

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, 1128 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, 1255 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 023 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 .