# API Reference Complete reference for all 14 endpoints across the four SentryAgent.ai AgentIdP services. ## Base URL ``` http://localhost:3000/api/v1 ``` The port is configured via the `PORT` environment variable (default: `3000`). All endpoints are currently unversioned within the path prefix `/api/v1`. API versioning will be introduced in Phase 2. ## Authentication All endpoints require a JWT Bearer token in the `Authorization` header: ``` Authorization: Bearer ``` Obtain a token via `POST /token` using your agent's `client_id` and `client_secret`. ## Table of Contents - [Errors](#errors) - [Agent Registry](#agent-registry) — 5 endpoints - [OAuth 2.0 Tokens](#oauth-20-tokens) — 3 endpoints - [Credential Management](#credential-management) — 4 endpoints - [Audit Log](#audit-log) — 2 endpoints --- ## Errors All error responses use this envelope: ```json { "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 | ### 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. --- ## Agent Registry ### POST /agents — Register a new agent 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 | | `agentType` | enum | Yes | `screener` \| `classifier` \| `orchestrator` \| `extractor` \| `summarizer` \| `router` \| `monitor` \| `custom` | | `version` | string | Yes | Semantic version (e.g. `1.0.0`) | | `capabilities` | string[] | Yes | `resource:action` strings, min 1 | | `owner` | string | Yes | Owning team/org, 1–128 chars | | `deploymentEnv` | enum | Yes | `development` \| `staging` \| `production` | **Response codes**: | Code | Meaning | |------|---------| | `201` | Agent registered successfully | | `400` | Validation error | | `401` | Invalid token | | `403` | Insufficient scope or free tier limit reached | | `409` | Email already registered | | `429` | Rate limit exceeded | **Example**: ```bash 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 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 | **Response codes**: | Code | Meaning | |------|---------| | `200` | List returned | | `400` | Invalid query parameters | | `401` | Invalid token | | `403` | Insufficient scope | | `429` | Rate limit exceeded | **Example**: ```bash 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 Returns the full identity record for a single agent. **Auth**: Bearer token with `agents:read` scope. **Path parameters**: | Parameter | Type | Description | |-----------|------|-------------| | `agentId` | UUID | The agent's immutable identifier | **Response codes**: | Code | Meaning | |------|---------| | `200` | Agent record returned | | `401` | Invalid token | | `403` | Insufficient scope | | `404` | Agent not found | | `429` | Rate limit exceeded | **Example**: ```bash curl -s "http://localhost:3000/api/v1/agents/$AGENT_ID" \ -H "Authorization: Bearer $TOKEN" | jq . ``` --- ### PATCH /agents/{agentId} — Update agent metadata Partially updates agent metadata. Only provided fields are changed. Immutable fields (`agentId`, `email`, `createdAt`) cannot be updated. **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 the full list) | | `owner` | string | Updated owner | | `deploymentEnv` | enum | Updated deployment environment | | `status` | enum | Updated status (`active` \| `suspended` \| `decommissioned`) | > Setting `status` to `decommissioned` is **irreversible**. The agent cannot be reactivated. **Response codes**: | Code | Meaning | |------|---------| | `200` | Agent updated, full record returned | | `400` | Validation error or attempt to modify immutable field | | `401` | Invalid token | | `403` | Insufficient scope or agent is decommissioned | | `404` | Agent not found | | `429` | Rate limit exceeded | **Example**: ```bash 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 Permanently decommissions an agent (soft delete). All active credentials are immediately revoked. This operation is **irreversible**. **Auth**: Bearer token with `agents:write` scope. **Response codes**: | Code | Meaning | |------|---------| | `204` | Agent decommissioned (no body) | | `401` | Invalid token | | `403` | Insufficient scope | | `404` | Agent not found | | `409` | Agent already decommissioned | | `429` | Rate limit exceeded | **Example**: ```bash curl -s -X DELETE "http://localhost:3000/api/v1/agents/$AGENT_ID" \ -H "Authorization: Bearer $TOKEN" \ -o /dev/null -w "%{http_code}\n" ``` --- ## OAuth 2.0 Tokens ### POST /token — Issue an access token Issues a signed RS256 JWT via the OAuth 2.0 Client Credentials grant. **Auth**: Client credentials in the request body (no Bearer token required for this endpoint). > **Content-Type**: This endpoint uses `application/x-www-form-urlencoded`, not JSON. **Request fields** (form-encoded): | Field | Required | Description | |-------|----------|-------------| | `grant_type` | Yes | Must be `client_credentials` | | `client_id` | Yes | Your agent's `agentId` (UUID) | | `client_secret` | Yes | The credential secret | | `scope` | No | Space-separated scopes. If omitted, all scopes are granted. | **Response codes**: | Code | Meaning | |------|---------| | `200` | Token issued | | `400` | Malformed request, invalid scope, or unsupported grant type | | `401` | Invalid `client_id` or `client_secret` | | `403` | Agent suspended or monthly token limit reached | | `429` | Rate limit exceeded | **Note on 429**: The `X-RateLimit-*` headers are returned on all responses, including `429`. **Example**: ```bash 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 Checks whether a token is active. Returns `{ "active": false }` for expired or revoked tokens — always `200 OK`. **Auth**: Bearer token with `tokens:read` scope. > **Content-Type**: `application/x-www-form-urlencoded` **Request fields**: | Field | Required | Description | |-------|----------|-------------| | `token` | Yes | The JWT to introspect | | `token_type_hint` | No | Optional hint — `access_token` | **Response codes**: | Code | Meaning | |------|---------| | `200` | Result returned (check `active` field) | | `400` | Missing `token` parameter | | `401` | Caller's Bearer token is invalid | | `403` | Caller's token lacks `tokens:read` scope | | `429` | Rate limit exceeded | **Example**: ```bash 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 Immediately invalidates a token. Idempotent — revoking an already-revoked token returns `200`. **Auth**: Bearer token (agent can revoke its own tokens). > **Content-Type**: `application/x-www-form-urlencoded` **Request fields**: | Field | Required | Description | |-------|----------|-------------| | `token` | Yes | The JWT to revoke | | `token_type_hint` | No | Optional hint — `access_token` | **Response codes**: | Code | Meaning | |------|---------| | `200` | Token revoked (or was already inactive) | | `400` | Missing `token` parameter | | `401` | Caller's Bearer token is invalid | | `403` | Insufficient permissions to revoke this token | | `429` | Rate limit exceeded | **Example**: ```bash 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 . ``` --- ## Credential Management ### POST /agents/{agentId}/credentials — Generate credentials 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. If omitted, credential does not expire. | **Response codes**: | Code | Meaning | |------|---------| | `201` | Credential created — save `clientSecret` now | | `400` | Invalid `expiresAt` | | `401` | Invalid token | | `403` | Insufficient scope or agent not active | | `404` | Agent not found | | `429` | Rate limit exceeded | **Example**: ```bash 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 Returns all credentials (active and revoked). The `clientSecret` is never returned. **Auth**: Bearer token with `agents:read` scope. **Query parameters**: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `page` | integer | 1 | Page number | | `limit` | integer | 20 | Results per page (max 100) | | `status` | enum | — | Filter by `active` or `revoked` | **Response codes**: | Code | Meaning | |------|---------| | `200` | List returned | | `400` | Invalid query parameters | | `401` | Invalid token | | `403` | Insufficient scope | | `404` | Agent not found | | `429` | Rate limit exceeded | **Example**: ```bash 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 Replaces the `clientSecret` for the same `credentialId`. The old secret is immediately invalidated. **Auth**: Bearer token with `agents:write` scope. **Request body** (`application/json`) — optional: | Field | Type | Required | Description | |-------|------|----------|-------------| | `expiresAt` | ISO 8601 | No | New expiry for the rotated credential | **Response codes**: | Code | Meaning | |------|---------| | `200` | Credential rotated — save new `clientSecret` now | | `400` | Invalid `expiresAt` | | `401` | Invalid token | | `403` | Insufficient scope | | `404` | Agent or credential not found | | `409` | Credential is already revoked | | `429` | Rate limit exceeded | **Example**: ```bash 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 Permanently revokes a credential. The credential can no longer obtain tokens. Irreversible. **Auth**: Bearer token with `agents:write` scope. **Response codes**: | Code | Meaning | |------|---------| | `204` | Credential revoked (no body) | | `401` | Invalid token | | `403` | Insufficient scope | | `404` | Agent or credential not found | | `409` | Credential already revoked | | `429` | Rate limit exceeded | **Example**: ```bash 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" ``` --- ## Audit Log ### GET /audit — Query audit log 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` | enum | — | Filter by action type (see [Audit Log guide](guides/query-audit-logs.md)) | | `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 codes**: | Code | Meaning | |------|---------| | `200` | Events returned | | `400` | Invalid parameters or date outside retention window | | `401` | Invalid token | | `403` | Token lacks `audit:read` scope | | `429` | Rate limit exceeded | **Example**: ```bash 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 Returns a single audit event by its immutable `eventId`. **Auth**: Bearer token with `audit:read` scope. **Path parameters**: | Parameter | Type | Description | |-----------|------|-------------| | `eventId` | UUID | The audit event's identifier | **Response codes**: | Code | Meaning | |------|---------| | `200` | Audit event returned | | `401` | Invalid token | | `403` | Token lacks `audit:read` scope | | `404` | Event not found or outside 90-day retention window | | `429` | Rate limit exceeded | **Example**: ```bash curl -s "http://localhost:3000/api/v1/audit/$EVENT_ID" \ -H "Authorization: Bearer $TOKEN" | jq . ```