openapi: 3.0.3 info: title: SentryAgent.ai — OAuth 2.0 Token Service version: 1.0.0 description: | The OAuth 2.0 Token Service provides agent authentication via the **Client Credentials grant** (RFC 6749 Section 4.4). It issues signed JWT access tokens, supports token introspection (RFC 7662), and token revocation (RFC 7009). Agents authenticate using their `client_id` (= `agentId`) and `client_secret` obtained during credential provisioning. **Supported Grant Type**: `client_credentials` only. All other grant types are rejected with `unsupported_grant_type`. **Token Lifetime**: 3600 seconds (1 hour) by default. **Scopes**: | Scope | Description | |-------|-------------| | `agents:read` | Read agent identity records | | `agents:write` | Create, update, and deactivate agent records | | `tokens:read` | Introspect tokens | | `audit:read` | Query the audit log | **Rate Limit**: 100 requests/minute per `client_id`. **Free Tier**: 10,000 token requests per month. servers: - url: http://localhost:3000/api/v1 description: Local development server - url: https://api.sentryagent.ai/v1 description: Production server tags: - name: OAuth 2.0 Tokens description: Token issuance, introspection, and revocation components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: | JWT access token obtained via `POST /token`. Required for `/token/introspect` and `/token/revoke`. BasicAuth: type: http scheme: basic description: | HTTP Basic authentication using `client_id` as the username and `client_secret` as the password. Used as an alternative credential method for token endpoint requests (in addition to request body). schemas: GrantType: type: string description: OAuth 2.0 grant type. Only `client_credentials` is supported. enum: - client_credentials example: client_credentials Scope: type: string description: | Space-separated list of requested OAuth 2.0 scopes. Available scopes: `agents:read`, `agents:write`, `tokens:read`, `audit:read`. pattern: '^(agents:read|agents:write|tokens:read|audit:read)(\s(agents:read|agents:write|tokens:read|audit:read))*$' example: "agents:read agents:write" TokenRequest: type: object description: | OAuth 2.0 Client Credentials token request body. Credentials may be provided in the request body (as `client_id` + `client_secret`) or via HTTP Basic authentication header. required: - grant_type properties: grant_type: $ref: '#/components/schemas/GrantType' client_id: type: string format: uuid description: > The agent's `agentId` (UUID). Required if not using HTTP Basic auth. example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" client_secret: type: string description: > The agent's client secret. Required if not using HTTP Basic auth. Treated as a sensitive value — never logged or stored in plain text. format: password example: "sk_live_7f3a2b1c9d8e4f0a6b5c3d2e1f0a9b8c" scope: $ref: '#/components/schemas/Scope' TokenResponse: type: object description: Successful OAuth 2.0 token response. required: - access_token - token_type - expires_in - scope properties: access_token: type: string description: > Signed JWT access token. Include this value in the `Authorization` header as `Bearer ` when calling other API endpoints. example: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAiLCJjbGllbnRfaWQiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAiLCJzY29wZSI6ImFnZW50czpyZWFkIGFnZW50czp3cml0ZSIsImlhdCI6MTc0MzE1MTIwMCwiZXhwIjoxNzQzMTU0ODAwfQ.signature" token_type: type: string description: Token type. Always `Bearer`. enum: - Bearer example: "Bearer" expires_in: type: integer description: Token lifetime in seconds from the time of issuance. Default is `3600`. example: 3600 scope: type: string description: Space-separated list of scopes granted by this token. example: "agents:read agents:write" OAuth2ErrorResponse: type: object description: | OAuth 2.0 error response as defined in RFC 6749 Section 5.2. Used exclusively for token endpoint errors. required: - error - error_description properties: error: type: string description: > Machine-readable OAuth 2.0 error code. enum: - invalid_request - invalid_client - invalid_grant - unauthorized_client - unsupported_grant_type - invalid_scope example: "invalid_client" error_description: type: string description: Human-readable description of the error. example: "Client authentication failed. Invalid client_id or client_secret." IntrospectRequest: type: object description: Token introspection request (RFC 7662). required: - token properties: token: type: string description: The token to introspect. example: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAifQ.signature" token_type_hint: type: string description: > Optional hint about the type of token being introspected. Currently only `access_token` is supported. enum: - access_token example: "access_token" IntrospectResponse: type: object description: | Token introspection response (RFC 7662). When `active` is `false`, no other fields are guaranteed to be present. required: - active properties: active: type: boolean description: > Whether the token is currently active (valid, not expired, not revoked). example: true sub: type: string format: uuid description: Subject — the `agentId` the token was issued for. example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" client_id: type: string format: uuid description: The `client_id` (agentId) that requested the token. example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" scope: type: string description: Space-separated list of scopes granted by this token. example: "agents:read agents:write" token_type: type: string description: Token type. Always `Bearer` for active tokens. example: "Bearer" iat: type: integer description: Unix timestamp (seconds) when the token was issued. example: 1743151200 exp: type: integer description: Unix timestamp (seconds) when the token expires. example: 1743154800 RevokeRequest: type: object description: Token revocation request (RFC 7009). required: - token properties: token: type: string description: The token to revoke. example: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAifQ.signature" token_type_hint: type: string description: Optional hint about the token type. enum: - access_token example: "access_token" ErrorResponse: type: object description: Standard error response envelope used across all SentryAgent.ai APIs. required: - code - message properties: code: type: string description: Machine-readable error code. example: "UNAUTHORIZED" message: type: string description: Human-readable description of the error. example: "A valid Bearer token is required." details: type: object description: Optional structured details providing additional context. additionalProperties: true example: {} responses: Unauthorized: description: Missing or invalid Bearer token. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "UNAUTHORIZED" message: "A valid Bearer token is required to access this resource." TooManyRequests: description: Rate limit exceeded. Retry after the reset time. headers: X-RateLimit-Limit: schema: type: integer description: Maximum requests allowed per minute. example: 100 X-RateLimit-Remaining: schema: type: integer description: Requests remaining in the current window. example: 0 X-RateLimit-Reset: schema: type: integer description: Unix timestamp when the rate limit window resets. example: 1743155400 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "RATE_LIMIT_EXCEEDED" message: "Too many requests. Please retry after the rate limit window resets." InternalServerError: description: Unexpected server error. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "INTERNAL_SERVER_ERROR" message: "An unexpected error occurred. Please try again later." paths: /token: post: operationId: issueToken tags: - OAuth 2.0 Tokens summary: Issue an access token (Client Credentials) description: | Issues a signed JWT access token for an agent using the OAuth 2.0 **Client Credentials grant** (RFC 6749 §4.4). The agent authenticates by providing its `client_id` (agentId) and `client_secret`. Credentials may be passed either: - In the **request body** (`client_id` + `client_secret` fields), or - Via **HTTP Basic authentication** header (username = `client_id`, password = `client_secret`). The token is a signed JWT containing the agent's identity claims. Use it as a `Bearer` token on subsequent API calls. **Free Tier Limit**: 10,000 token requests per month. Exceeding this returns `403` with `FREE_TIER_LIMIT_EXCEEDED`. requestBody: required: true content: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/TokenRequest' example: grant_type: client_credentials client_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" client_secret: "sk_live_7f3a2b1c9d8e4f0a6b5c3d2e1f0a9b8c" scope: "agents:read agents:write" responses: '200': description: Access token issued successfully. headers: X-RateLimit-Limit: schema: type: integer example: 100 X-RateLimit-Remaining: schema: type: integer example: 99 X-RateLimit-Reset: schema: type: integer example: 1743155400 Cache-Control: schema: type: string description: Token responses must not be cached. example: "no-store" Pragma: schema: type: string example: "no-cache" content: application/json: schema: $ref: '#/components/schemas/TokenResponse' example: access_token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAiLCJjbGllbnRfaWQiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAiLCJzY29wZSI6ImFnZW50czpyZWFkIGFnZW50czp3cml0ZSIsImlhdCI6MTc0MzE1MTIwMCwiZXhwIjoxNzQzMTU0ODAwfQ.signature" token_type: "Bearer" expires_in: 3600 scope: "agents:read agents:write" '400': description: Malformed or missing required request parameters. content: application/json: schema: $ref: '#/components/schemas/OAuth2ErrorResponse' examples: missingGrantType: summary: Missing grant_type value: error: "invalid_request" error_description: "The 'grant_type' parameter is required." invalidScope: summary: Invalid scope requested value: error: "invalid_scope" error_description: "Requested scope 'admin:all' is not available." unsupportedGrantType: summary: Unsupported grant type value: error: "unsupported_grant_type" error_description: "Only 'client_credentials' grant type is supported." '401': description: Client authentication failed. Invalid `client_id` or `client_secret`. content: application/json: schema: $ref: '#/components/schemas/OAuth2ErrorResponse' example: error: "invalid_client" error_description: "Client authentication failed. Invalid client_id or client_secret." '403': description: > Client is not authorised to request a token. May indicate the agent is suspended, decommissioned, or the free tier monthly limit has been reached. content: application/json: schema: $ref: '#/components/schemas/OAuth2ErrorResponse' examples: agentSuspended: summary: Agent is suspended value: error: "unauthorized_client" error_description: "Agent is currently suspended and cannot obtain tokens." freeTierLimit: summary: Monthly token limit reached value: error: "unauthorized_client" error_description: "Free tier monthly token limit of 10,000 requests has been reached." '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' /token/introspect: post: operationId: introspectToken tags: - OAuth 2.0 Tokens summary: Introspect a token (RFC 7662) description: | Determines whether a given access token is currently active (valid, not expired, not revoked). Returns the token's metadata if active. Compliant with RFC 7662 (OAuth 2.0 Token Introspection). The caller must present a valid Bearer token with `tokens:read` scope to use this endpoint. security: - BearerAuth: [] requestBody: required: true content: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/IntrospectRequest' example: token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAifQ.signature" token_type_hint: "access_token" responses: '200': description: | Token introspection result. Note: a `200` response is returned even for inactive tokens — check the `active` field to determine token validity. headers: X-RateLimit-Limit: schema: type: integer example: 100 X-RateLimit-Remaining: schema: type: integer example: 98 X-RateLimit-Reset: schema: type: integer example: 1743155400 content: application/json: schema: $ref: '#/components/schemas/IntrospectResponse' examples: activeToken: summary: Active token value: active: true sub: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" client_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" scope: "agents:read agents:write" token_type: "Bearer" iat: 1743151200 exp: 1743154800 inactiveToken: summary: Inactive (expired or revoked) token value: active: false '400': description: Missing or malformed `token` parameter. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "VALIDATION_ERROR" message: "The 'token' parameter is required." '401': $ref: '#/components/responses/Unauthorized' '403': description: Caller's token does not have the `tokens:read` scope. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "INSUFFICIENT_SCOPE" message: "The 'tokens:read' scope is required to introspect tokens." '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' /token/revoke: post: operationId: revokeToken tags: - OAuth 2.0 Tokens summary: Revoke a token (RFC 7009) description: | Revokes an access token, immediately invalidating it for all subsequent requests. Compliant with RFC 7009 (OAuth 2.0 Token Revocation). Revoking an already-revoked or expired token is considered a success (idempotent operation per RFC 7009 §2.1). The caller must present a valid Bearer token to revoke another token. An agent may revoke its own tokens; admin scope is required to revoke tokens belonging to other agents. security: - BearerAuth: [] requestBody: required: true content: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/RevokeRequest' example: token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWIyYzNkNC1lNWY2LTc4OTAtYWJjZC1lZjEyMzQ1Njc4OTAifQ.signature" token_type_hint: "access_token" responses: '200': description: | Token revoked successfully (or was already inactive). Per RFC 7009, revocation always returns `200` for any valid request, even if the token was already revoked or expired. headers: X-RateLimit-Limit: schema: type: integer example: 100 X-RateLimit-Remaining: schema: type: integer example: 97 X-RateLimit-Reset: schema: type: integer example: 1743155400 content: application/json: schema: type: object properties: {} example: {} '400': description: Missing or malformed `token` parameter. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "VALIDATION_ERROR" message: "The 'token' parameter is required." '401': $ref: '#/components/responses/Unauthorized' '403': description: Insufficient permissions to revoke this token. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "FORBIDDEN" message: "You do not have permission to revoke this token." '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError'