From 6a93acafb237a76e03f38d811822a2f6fbaa782c Mon Sep 17 00:00:00 2001 From: armandokun Date: Mon, 1 Jun 2026 14:23:49 +0300 Subject: [PATCH] docs: document named API keys Add the /v1/api-keys endpoints (list/create/revoke) and ApiKey schemas to the OpenAPI generator and regenerate. Explain multiple/named keys in the authentication page and note round-robin is unnecessary since limits are per inbox. Co-authored-by: Cursor --- api-reference/authentication.mdx | 14 ++- api-reference/openapi.json | 162 +++++++++++++++++++++++++++++++ scripts/generate-openapi.js | 99 +++++++++++++++++++ 3 files changed, 273 insertions(+), 2 deletions(-) diff --git a/api-reference/authentication.mdx b/api-reference/authentication.mdx index 4b98726..6850a15 100644 --- a/api-reference/authentication.mdx +++ b/api-reference/authentication.mdx @@ -20,11 +20,21 @@ API keys are prefixed with `om_` for production. See [Quickstart](/quickstart) for the full setup flow. +## Multiple API keys + +Your account has a primary key (shown in the dashboard), and you can issue additional **named** keys via the API: + +- `POST /v1/api-keys` with `{ "name": "..." }` creates a key and returns its value **once** — store it securely. +- `GET /v1/api-keys` lists your keys with masked previews (the full value is never returned again). +- `DELETE /v1/api-keys/{id}` revokes a key immediately. + +Issue separate keys per environment or per fleet so you can revoke one without disrupting the others. You don't need to round-robin keys to scale — [rate limits](/concepts/rate-limits) are mostly per inbox, so spread load across inboxes instead. + ## Security -- Keep your API key secret. Do not expose it in client-side code. +- Keep your API keys secret. Do not expose them in client-side code. - Each API key is scoped to a single account. Requests authenticate to the account that owns the key. -- If you believe your key has been compromised, contact support immediately for rotation. +- If you believe a key has been compromised, revoke it with `DELETE /v1/api-keys/{id}` (or rotate the primary key from the dashboard). ## Invalid authentication diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 0f05cd1..9ddf07f 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -413,6 +413,125 @@ } } }, + "/v1/api-keys": { + "get": { + "summary": "List API keys", + "description": "List the account's named API keys. The full key value is never returned — only a masked preview.", + "operationId": "listApiKeys", + "responses": { + "200": { + "description": "API keys", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiKey" + } + }, + "total": { + "type": "integer" + } + } + } + } + } + } + } + }, + "post": { + "summary": "Create API key", + "description": "Create a new named API key. The full key is returned exactly once in this response — store it securely. Use this to issue multiple scoped keys (e.g. one per environment or fleet) instead of round-robining a single key.", + "operationId": "createApiKey", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + } + } + } + } + }, + "responses": { + "201": { + "description": "API key created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiKeyWithSecret" + } + } + } + }, + "400": { + "description": "Missing name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "422": { + "description": "Per-account API key limit reached", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/v1/api-keys/{id}": { + "delete": { + "summary": "Revoke API key", + "description": "Revoke a named API key. Idempotent; already-revoked keys return 204.", + "operationId": "revokeApiKey", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "API key revoked" + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, "/v1/usage": { "get": { "summary": "Get usage", @@ -1064,6 +1183,49 @@ } } }, + "ApiKey": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "keyPreview": { + "type": "string", + "description": "Masked preview, e.g. om_****1a2b" + }, + "lastUsedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiKeyWithSecret": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string", + "description": "Full key — returned only on creation" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, "InboxUsage": { "type": "object", "properties": { diff --git a/scripts/generate-openapi.js b/scripts/generate-openapi.js index 2e1bb18..4f6a18b 100644 --- a/scripts/generate-openapi.js +++ b/scripts/generate-openapi.js @@ -255,6 +255,86 @@ const spec = { }, }, }, + "/v1/api-keys": { + get: { + summary: "List API keys", + description: + "List the account's named API keys. The full key value is never returned — only a masked preview.", + operationId: "listApiKeys", + responses: { + "200": { + description: "API keys", + content: { + "application/json": { + schema: { + type: "object", + properties: { + data: { + type: "array", + items: { $ref: "#/components/schemas/ApiKey" }, + }, + total: { type: "integer" }, + }, + }, + }, + }, + }, + }, + }, + post: { + summary: "Create API key", + description: + "Create a new named API key. The full key is returned exactly once in this response — store it securely. Use this to issue multiple scoped keys (e.g. one per environment or fleet) instead of round-robining a single key.", + operationId: "createApiKey", + requestBody: { + required: true, + content: { + "application/json": { + schema: { + type: "object", + required: ["name"], + properties: { + name: { type: "string", minLength: 1, maxLength: 100 }, + }, + }, + }, + }, + }, + responses: { + "201": { + description: "API key created", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/ApiKeyWithSecret" }, + }, + }, + }, + "400": { + description: "Missing name", + content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }, + }, + "422": { + description: "Per-account API key limit reached", + content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }, + }, + }, + }, + }, + "/v1/api-keys/{id}": { + delete: { + summary: "Revoke API key", + description: "Revoke a named API key. Idempotent; already-revoked keys return 204.", + operationId: "revokeApiKey", + parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }], + responses: { + "204": { description: "API key revoked" }, + "404": { + description: "Not found", + content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }, + }, + }, + }, + }, "/v1/usage": { get: { summary: "Get usage", @@ -614,6 +694,25 @@ const spec = { createdAt: { type: "string", format: "date-time" }, }, }, + ApiKey: { + type: "object", + properties: { + id: { type: "string" }, + name: { type: "string" }, + keyPreview: { type: "string", description: "Masked preview, e.g. om_****1a2b" }, + lastUsedAt: { type: "string", format: "date-time", nullable: true }, + createdAt: { type: "string", format: "date-time" }, + }, + }, + ApiKeyWithSecret: { + type: "object", + properties: { + id: { type: "string" }, + name: { type: "string" }, + key: { type: "string", description: "Full key — returned only on creation" }, + createdAt: { type: "string", format: "date-time" }, + }, + }, InboxUsage: { type: "object", properties: {