Message API/docs/components/api-key-management/api

REST API

Route and payload reference for internal API key administration.

Routes

These routes manage client API keys through an internal admin experience.

  • Admin auth header: include `x-admin-key: ADMIN_API_KEY` on every route.
  • Backend prerequisite: define `ADMIN_API_KEY=your-key` in the DB backend `.env` before calling these routes.
  • List: returns masked metadata only.
  • Create: returns masked metadata plus the raw `apiKey` once.
  • Rotate: returns masked metadata plus the replacement raw `apiKey` once.
  • Delete: permanently removes the client key.
GET    /admin/api-keys
POST   /admin/api-keys
GET    /admin/api-keys/:id
PATCH  /admin/api-keys/:id
POST   /admin/api-keys/:id/rotate
DELETE /admin/api-keys/:id
Routes

Request Contracts

The create and update routes accept a name, active flag, and optional expiry date. The rotate route only needs the admin header.

Create Key

async function createApiKey() {
  const response = await fetch('/api/admin/api-keys', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-admin-key': ADMIN_API_KEY,
    },
    body: JSON.stringify({
      name: 'Main Frontend Key',
      isActive: true,
      expiresAt: null,
    }),
  });

  if (!response.ok) {
    throw new Error(`Failed to create API key: ${response.status}`);
  }

  return response.json();
}

Update Key

async function updateApiKey(id: number) {
  const response = await fetch(`/api/admin/api-keys/${id}`, {
    method: 'PATCH',
    headers: {
      'Content-Type': 'application/json',
      'x-admin-key': ADMIN_API_KEY,
    },
    body: JSON.stringify({
      name: 'Backup Key',
      isActive: false,
      expiresAt: null,
    }),
  });

  if (!response.ok) {
    throw new Error(`Failed to update API key: ${response.status}`);
  }

  return response.json();
}

Rotate Key

async function rotateApiKey(id: number) {
  const response = await fetch(`/api/admin/api-keys/${id}/rotate`, {
    method: 'POST',
    headers: {
      'x-admin-key': ADMIN_API_KEY,
    },
  });

  if (!response.ok) {
    throw new Error(`Failed to rotate API key: ${response.status}`);
  }

  return response.json();
}
type CreateApiKeyRequest = {
  name?: string;
  isActive?: boolean;
  expiresAt?: string | null;
};

type UpdateApiKeyRequest = {
  name?: string;
  isActive?: boolean;
  expiresAt?: string | null;
};
Request Contracts

Response Contracts

Create and rotate responses expose the raw key once. Later list and get operations expose only masked or prefixed values.

  • Raw key handling: save `apiKey` immediately because the backend may not return it again.
  • Masked list responses: treat `maskedKey` and `keyPrefix` as display-only metadata.
  • Client usage: send the saved raw key later as `x-api-key` on protected business routes.
type ApiKeyRecord = {
  id: number;
  name: string;
  keyPrefix: string;
  maskedKey: string;
  isActive: boolean;
  expiresAt: string | null;
  lastUsedAt: string | null;
  createdAt: string;
  updatedAt: string;
};

type CreateOrRotateApiKeyResponse = {
  message: string;
  apiKey: string;
  key: ApiKeyRecord;
};
Response Contracts

Backend Behavior

Handle these backend outcomes explicitly in the client admin experience.

  • 401/403: missing or invalid `x-admin-key`.
  • 404 Not Found: requested key record does not exist.
  • 400 Bad Request: invalid create or update payload.
  • One-time raw key: create and rotate are the only reliable moments to capture the full client key.