Skip to main content
Home›Docs›API Reference›API keys and OAuth
API Reference

API keys and OAuth

Manage workspace API keys and register OAuth 2.1 clients for MCP access to the Perceive8 API.

API keys and OAuth

API keys (pk_live_…) authenticate server-to-server calls to the Perceive8 API; this page covers creating, listing, and deleting them. The same service also hosts an OAuth 2.1 authorization server (PKCE authorization_code and client_credentials grants) that issues JWT access tokens — primarily used by MCP clients such as Claude Desktop and Cursor. Base URL for all endpoints: https://api.perceive8.com.

API keys

Key management endpoints operate on the current workspace. Select the workspace with the X-Workspace-Id header; when omitted, your Personal workspace is used. All three endpoints require the admin workspace role (owners always qualify).

GET /v1/keys

List all API keys for the authenticated workspace, newest first.

curl https://api.perceive8.com/v1/keys \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx" \
  -H "X-Workspace-Id: 7c9e6679-7425-40de-944b-e07fc1f90ae7"

Response — 200 OK, an array of key objects:

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "production-backend",
    "key_prefix": "pk_live_ab",
    "scopes": ["*"],
    "rate_limit_rpm": 60,
    "is_active": true,
    "last_used_at": "2026-08-18T14:02:11Z",
    "expires_at": null,
    "created_at": "2026-07-01T09:30:00Z"
  }
]
Field Type Description
id string (UUID) Key identifier, used for deletion.
name string Human-readable label.
key_prefix string First 10 characters of the key, for identification only.
scopes array of strings Scopes granted to the key. ["*"] means unrestricted.
rate_limit_rpm integer Per-key request budget, in requests per minute.
is_active boolean Whether the key can authenticate.
last_used_at string (datetime) or null Last successful authentication timestamp.
expires_at string (datetime) or null Expiry timestamp; null means the key does not expire.
created_at string (datetime) Creation timestamp.

The full key value is never returned by this endpoint — only the prefix.

POST /v1/keys

Create a new API key for the authenticated workspace. Requires the admin workspace role.

Request body

{
  "name": "ci-pipeline",
  "scopes": ["*"],
  "rate_limit_rpm": 120,
  "expires_at": "2027-01-01T00:00:00Z"
}
Field Type Default Description
name string — (required) Human-readable label for the key.
scopes array of strings ["*"] Scopes to grant. The default ["*"] is unrestricted.
rate_limit_rpm integer 60 Requests-per-minute limit enforced for this key.
expires_at string (datetime) or null null Optional expiry timestamp (ISO 8601).

Response — 200 OK. The creation response contains every field from GET /v1/keys plus the key field with the full plaintext key:

{
  "id": "9b2f7c1e-2d4a-4f8e-9c1d-5e6a7b8c9d0e",
  "name": "ci-pipeline",
  "key_prefix": "pk_live_xY",
  "scopes": ["*"],
  "rate_limit_rpm": 120,
  "is_active": true,
  "last_used_at": null,
  "expires_at": "2027-01-01T00:00:00Z",
  "created_at": "2026-08-19T06:00:00Z",
  "key": "pk_live_xY4…"
}

The key value is returned only once, in this response. Perceive8 stores only a bcrypt hash of the key, so it cannot be recovered later — copy it immediately. New keys always carry the pk_live_ prefix.

curl -X POST https://api.perceive8.com/v1/keys \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-pipeline", "rate_limit_rpm": 120}'

DELETE /v1/keys/{key_id}

Delete an API key. The key must belong to the authenticated workspace. Requires the admin workspace role.

Parameters

Name In Type Description
key_id path string (UUID) ID of the key to delete.

Response — 204 No Content on success. Returns 404 ({"detail": "API key not found"}) when no key with that ID exists in the current workspace.

curl -X DELETE https://api.perceive8.com/v1/keys/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx"

OAuth

Perceive8 implements an OAuth 2.1 authorization server: RFC 8414 metadata discovery, an RFC 7636 PKCE authorization_code flow for interactive (MCP) clients, an RFC 6749 client_credentials grant for server-to-server clients, and RFC 7662 token introspection. Access tokens are HS256 JWTs with a 1-hour lifetime; the token payload carries sub (user ID), scopes, token_type (oauth2), iat, exp, and aud (authenticated).

Endpoints that act on behalf of a user (/authorize, /clients) require a user JWT in the Authorization: Bearer header — API keys are explicitly rejected there with 403. Register and manage clients from the web app or via the API below.

GET /v1/oauth/metadata

Authorization server metadata (RFC 8414). Public — no authentication required. MCP clients fetch this document to discover the authorization and token endpoints.

Response — 200 OK:

{
  "issuer": "https://api.perceive8.com",
  "authorization_endpoint": "https://api.perceive8.com/v1/oauth/authorize",
  "token_endpoint": "https://api.perceive8.com/v1/oauth/token",
  "introspection_endpoint": "https://api.perceive8.com/v1/oauth/introspect",
  "registration_endpoint": "https://api.perceive8.com/v1/oauth/clients",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "client_credentials"],
  "token_endpoint_auth_methods_supported": ["client_secret_post", "none"],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["mcp:analyses:read", "mcp:transcripts:read", "…"],
  "service_documentation": "https://perceive8.com/docs/mcp",
  "ui_locales_supported": ["en"]
}

scopes_supported lists the full MCP scope catalog (see GET /v1/oauth/scopes).

GET /.well-known/oauth-authorization-server

Serves the same RFC 8414 metadata document as GET /v1/oauth/metadata. Public. MCP clients that probe the well-known discovery URL are answered here.

GET /v1/oauth/authorize

Authorization endpoint for the OAuth 2.1 PKCE flow. The client redirects the user's browser here; on success the server redirects back to the client's redirect_uri with an authorization code. Requires an authenticated Perceive8 user (Authorization: Bearer <user JWT>); API keys are rejected with 403, and requests without valid credentials fail with 401.

Parameters

Name In Type Description
response_type query string Required. Must be code.
client_id query string Required. ID of a registered, active OAuth client.
redirect_uri query string Required. Must exactly match one of the client's registered redirect URIs (no substring or wildcard matching).
scope query string Optional space-separated scope list. Defaults to mcp:analyses:read mcp:reports:read mcp:query:execute. Each scope must be in the MCP catalog (or *).
state query string Optional opaque value echoed back to the client for CSRF protection.
code_challenge query string Required. Base64url-encoded SHA-256 of the code verifier, 43–128 characters (RFC 7636).
code_challenge_method query string Optional. Only S256 is supported.

Response — 302 Found redirect to redirect_uri with ?code=…&state=…. The authorization code is single-use and expires after 600 seconds.

Error handling: an unknown client_id, an invalid redirect_uri scheme, or a redirect_uri not registered for the client returns 400 directly (the server does not redirect to untrusted URIs). Other failures — a non-code response_type (unsupported_response_type), a non-S256 method or malformed challenge (invalid_request), or unknown scopes (invalid_scope) — return a 302 redirect to the registered redirect_uri with error and error_description query parameters (plus state when provided).

POST /v1/oauth/token

Token endpoint. Public — no Authorization header; clients authenticate with form fields. The request body must be application/x-www-form-urlencoded.

Grant types

grant_type Required form fields Notes
client_credentials client_id, client_secret Server-to-server. The issued token carries the scopes registered on the client (default ["*"]); the optional scope form field is accepted but ignored.
authorization_code code, redirect_uri, code_verifier PKCE flow. redirect_uri must match the value used at /authorize; code_verifier is checked against the stored S256 challenge. client_id is optional but validated against the code when present. Codes are single-use and expire after 600 seconds.

Response — 200 OK:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs…",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "mcp:analyses:read mcp:reports:read mcp:query:execute"
}

Error responses use RFC 6749 error codes in a JSON body: unsupported_grant_type (400), invalid_request (400, missing fields), invalid_client (401, unknown client or bad secret), invalid_grant (400, unknown/expired/used code, redirect URI mismatch, or PKCE verification failure).

curl -X POST https://api.perceive8.com/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=SplxlOBeZQQYbYS6WxSbIA" \
  -d "redirect_uri=http://localhost:8080/callback" \
  -d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

POST /v1/oauth/introspect

Token introspection (RFC 7662). Requires authentication — any valid credential (user JWT or API key) works. Form field: token (required).

Response — 200 OK. For a valid token:

{
  "active": true,
  "sub": "a1b2c3d4-0000-4000-8000-abcdefabcdef",
  "scopes": ["mcp:analyses:read", "mcp:reports:read"],
  "exp": 1787123456
}

Expired or otherwise invalid tokens return {"active": false} (still 200 OK).

POST /v1/oauth/clients

Register a new OAuth client. Requires a user JWT (Authorization: Bearer); API keys are rejected with 403.

Request body

{
  "name": "my-mcp-client",
  "scopes": ["mcp:analyses:read", "mcp:reports:read"],
  "redirect_uris": ["http://localhost:8080/callback"]
}
Field Type Default Description
name string — (required) Client display name.
scopes array of strings ["*"] Scopes the client may request.
redirect_uris array of strings [] Allowed redirect URIs for the PKCE flow. Each must have a valid URI scheme (http(s):// or a custom scheme).

Response — 200 OK. The response contains the client record plus client_secret:

{
  "id": "5f3a2b1c-1111-4222-8333-444455556666",
  "user_id": "a1b2c3d4-0000-4000-8000-abcdefabcdef",
  "name": "my-mcp-client",
  "client_id": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
  "scopes": ["mcp:analyses:read", "mcp:reports:read"],
  "redirect_uris": ["http://localhost:8080/callback"],
  "is_active": true,
  "created_at": "2026-08-19T06:00:00Z",
  "client_secret": "Zk9…"
}

The client_secret is shown only once, in this response — only its bcrypt hash is stored. Copy it immediately. Public PKCE clients that use authorization_code with token_endpoint_auth_method none do not need the secret.

GET /v1/oauth/clients

List your active OAuth clients. Requires a user JWT; API keys are rejected with 403.

Response — 200 OK, an array of client objects with the fields id, user_id, name, client_id, scopes, redirect_uris, is_active, created_at. The client_secret is never included.

GET /v1/oauth/scopes

Return the full MCP scope catalog with descriptions. Public — no authentication required. Use it to decide which scopes to request when registering a client or redirecting to /v1/oauth/authorize.

Response — 200 OK:

{
  "scopes": [
    { "scope": "mcp:analyses:read", "description": "Read meeting analyses and metadata" },
    { "scope": "mcp:transcripts:read", "description": "Read full meeting transcripts" }
  ]
}

Full catalog:

Scope Description
mcp:analyses:read Read meeting analyses and metadata
mcp:transcripts:read Read full meeting transcripts
mcp:reports:read Read analysis reports and sections
mcp:reports:write Generate new analysis reports
mcp:query:execute Execute natural language queries against transcripts
mcp:playbooks:read Read playbook templates and configurations
mcp:playbooks:write Clone and configure playbook templates
mcp:speakers:read Read speaker profiles and voice data
mcp:alerts:read Read meeting alerts
mcp:alerts:write Acknowledge and manage alerts
mcp:aria:read Read Aria conversation history
mcp:aria:write Send messages to Aria AI assistant
mcp:calendar:read Read calendar upcoming meetings and settings
mcp:calendar:write Manage calendar upcoming meetings and sync
mcp:business_profile:read Read the workspace business profile
mcp:business_profile:write Modify and reset the workspace business profile
mcp:agents:read Read agent metadata and runtime status
mcp:agents:write Provision, republish, archive, and manage agent secrets

MCP

Perceive8 exposes an MCP server at https://api.perceive8.com/mcp for AI clients (Claude Desktop, Cursor, and similar). MCP clients authenticate through the OAuth server documented on this page using the authorization_code + PKCE flow: they discover endpoints from /.well-known/oauth-authorization-server, redirect the user to /v1/oauth/authorize, and exchange the resulting code at /v1/oauth/token. The issued JWT carries sub (user ID) and the granted mcp:* scopes, and the MCP server enforces per-tool scope restrictions based on them. See the service_documentation URL in the server metadata (https://perceive8.com/docs/mcp) for client setup instructions.