MCP & Serving API Keys
This resource manages the workspace’s secret credentials. All of them are hashed at rest, returned in full exactly once at creation, may be given an expiry, and can be revoked at any time. They differ in what they authenticate:
| Type | Prefix | Scopes | Authenticates |
|---|---|---|---|
mcp | sk_ | MCP tool scopes, e.g. read:audience, trigger:sync | The MCP server |
rest_api | rk_ | RBAC permissions — see REST API keys | The REST API (/api/v1) |
serving | svk_ | serve:profile | The Profile API — attributes, and member audiences on request |
a2a | a2a_ | a2a:invoke | The A2A server |
Each type is accepted on its own surface and nowhere else. A credential presented to a surface it does not belong to is refused 401 on the class of the key, before its scopes are considered — so widening an sk_ key’s scopes can never make it read a profile or call the REST API.
An a2a key acts as the person who created it. It lets an outside agent use the workspace’s agent with that person’s own access, read fresh on every request — so revoking their access shrinks the key, and there is no separate grant to keep in step. It exists for automated suites and headless service integrations. Where a person is at the keyboard, have them sign in to the agent instead, so the work is attributed to them rather than to whoever minted the key. Its scope list is fixed at ["a2a:invoke"] and may be omitted when creating one.
read:store and read:membership are MCP tool scopes only. They gate the store and membership tools an agent may invoke over JSON-RPC. Neither authorises an HTTP read: the Profile API takes a serving key with serve:profile, and the Membership API takes a public membership key (pk_), which is a different resource entirely — see below.
Endpoints
The same endpoints manage every key type; pass ?type=rest_api, ?type=serving or ?type=a2a, or omit it for MCP keys (the default).
| Method | Path | Description |
|---|---|---|
GET | /api/v1/workspaces/{id}/api-keys | List MCP keys (add ?type=rest_api, ?type=serving or ?type=a2a for the others) |
POST | /api/v1/workspaces/{id}/api-keys | Create a new API key |
DELETE | /api/v1/workspaces/{id}/api-keys/{keyId} | Revoke an API key |
Membership keys are not managed here. The Membership API’s pk_ credential is public rather than secret — stored in the clear, readable for as long as it exists, and never expiring — so it is a separate resource under /api/v1/workspaces/{id}/membership-keys. Create and revoke them on Personalize → Membership → API keys.
List API Keys
GET /api/v1/workspaces/{id}/api-keys
Returns all API keys for the workspace. The raw key value is never returned in list responses — only the key_prefix is shown for identification.
Response
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "CI/CD Pipeline Key",
"type": "mcp",
"key_prefix": "sk_abc12",
"scopes": ["read:audience", "trigger:sync"],
"created_by": "770e8400-e29b-41d4-a716-446655440000",
"last_used_at": "2024-01-16T11:00:00Z",
"expires_at": null,
"created_at": "2024-01-15T09:30:00Z"
}
]Example
curl -X GET https://agentic.zeotap.com/api/v1/workspaces/{id}/api-keys \
-H "Authorization: Bearer <token>" \
-H "X-Workspace-ID: <workspace-id>"Create API Key
POST /api/v1/workspaces/{id}/api-keys
Creates a new API key for the workspace. The raw key value is returned only once in the creation response. Store it securely — it cannot be retrieved again.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name for the API key |
scopes | string[] | Yes, except for a2a | At least one scope, from the vocabulary of the key’s type. MCP keys use tool scopes (e.g. read:audience, trigger:sync); REST API keys use RBAC permissions; serving keys use serve:profile. An a2a key carries exactly ["a2a:invoke"], so the field may be omitted and anything else is refused |
type | string | No | mcp (default), rest_api, serving, or a2a |
expires_at | string | No | RFC3339 timestamp or YYYY-MM-DD date. Omit for a key that never expires |
{
"name": "CI/CD Pipeline Key",
"scopes": ["read:audience", "trigger:sync"]
}Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "CI/CD Pipeline Key",
"type": "mcp",
"raw_key": "sk_abc123def456ghi789...",
"key_prefix": "sk_abc12",
"scopes": ["read:audience", "trigger:sync"],
"created_by": "770e8400-e29b-41d4-a716-446655440000",
"expires_at": null,
"created_at": "2024-01-15T09:30:00Z"
}The raw_key field is only returned in the creation response. Copy and store it securely immediately. If you lose the key, you must revoke it and create a new one.
Example
curl -X POST https://agentic.zeotap.com/api/v1/workspaces/{id}/api-keys \
-H "Authorization: Bearer <token>" \
-H "X-Workspace-ID: <workspace-id>" \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline Key",
"scopes": ["read:audience", "trigger:sync"]
}'Revoke API Key
DELETE /api/v1/workspaces/{id}/api-keys/{keyId}
Permanently revokes an API key. Once revoked, any requests using this key will receive a 401 Unauthorized response. This action cannot be undone.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Workspace ID |
keyId | string (UUID) | API key ID to revoke |
Response
{
"status": "revoked"
}Example
curl -X DELETE https://agentic.zeotap.com/api/v1/workspaces/{id}/api-keys/{keyId} \
-H "Authorization: Bearer <token>" \
-H "X-Workspace-ID: <workspace-id>"API Key Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier |
workspace_id | string (UUID) | Workspace the key belongs to |
name | string | Display name |
type | string | mcp (sk_), rest_api (rk_), serving (svk_), or a2a (a2a_) |
raw_key | string | Raw key value (only in creation response) |
key_prefix | string | First few characters of the key for identification |
scopes | string[] | Scopes granted to the key |
created_by | string (UUID) | Account that created the key |
last_used_at | string (ISO 8601) or null | When the key was last used |
expires_at | string (ISO 8601) or null | Expiry timestamp, or null if it never expires |
created_at | string (ISO 8601) | Creation timestamp |
Best Practices
- Name keys descriptively — Use names like “Production Sync Service” or “CI/CD Pipeline” so you can identify each key’s purpose.
- One key per integration — Create separate keys for each system that needs API access. This makes it easy to revoke access for a single integration without affecting others.
- Rotate regularly — Create a new key, update your integration, then revoke the old key.
- Store securely — Use environment variables or a secrets manager. Never commit API keys to version control.