Skip to content

API keys

Create, list, and revoke the keys that authenticate every other route. These three routes are session-only.

Ten active keys at most. Format is bp_ plus 64 hex characters. Shown once. Hashed at rest. A lost key is gone.

MethodPathAuthWhat
POST/api/v1/api-keysSessionCreate
GET/api/v1/api-keysSessionList
DELETE/api/v1/api-keys/:idSessionRevoke
POST/api/v1/api-keys

Create a new API key. The full key value is returned only in this response, store it immediately. Session-authenticated, so this must be called from a signed-in browser. Maximum 10 active keys per user.

Request body

NameTypeRequiredDescription
namestringRequiredHuman-readable label for the key (1-100 characters)
permissionsstring[]OptionalPermissions to grant: read, write, delete Default ["read"].
scopesstring[]OptionalResource scopes to restrict access: articles, social, projects, user
expires_in_daysintegerOptionalKey expiration in days (1-365). Omit for no expiration.
Request
// Runs in a signed-in browser session. The auth cookie is the credential.
const res = await fetch('https://www.brainpercent.app/api/v1/api-keys', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({
    name: 'My App Key',
    permissions: ['read', 'write'],
    scopes: ['articles', 'social'],
    expires_in_days: 90,
  }),
});

const { data } = await res.json();
// data.key is the only time you will ever see the full key
console.log('API Key:', data.key);
201Response
{
  "success": true,
  "data": {
    "id": "b7e2f1a0-3c4d-4e5f-8a9b-0c1d2e3f4a5b",
    "name": "My App Key",
    "key": "bp_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
    "key_prefix": "bp_a1b2",
    "permissions": [
      "read",
      "write"
    ],
    "scopes": [
      "articles",
      "social"
    ],
    "created_at": "2026-02-01T12:00:00Z",
    "expires_at": "2026-05-02T12:00:00Z"
  }
}
GET/api/v1/api-keys

List the API keys on your account. Returns masked key prefixes only, never the full key. Session-authenticated.

Request
const res = await fetch('https://www.brainpercent.app/api/v1/api-keys', {
  credentials: 'include',
});
const { data } = await res.json();
console.log(`You have ${data.length} API key(s)`);
data.forEach(k => console.log(`  ${k.name} (${k.key_prefix}...) active=${k.is_active}`));
200Response
{
  "success": true,
  "data": [
    {
      "id": "b7e2f1a0-3c4d-4e5f-8a9b-0c1d2e3f4a5b",
      "name": "My App Key",
      "key_prefix": "bp_a1b2",
      "permissions": [
        "read",
        "write"
      ],
      "scopes": [
        "articles",
        "social"
      ],
      "created_at": "2026-01-15T10:00:00Z",
      "last_used_at": "2026-02-01T08:30:00Z",
      "expires_at": "2026-04-15T10:00:00Z",
      "is_active": true
    },
    {
      "id": "c8f3a2b1-4d5e-4f6a-9b0c-1d2e3f4a5b6c",
      "name": "CI/CD Pipeline",
      "key_prefix": "bp_c3d4",
      "permissions": [
        "read"
      ],
      "scopes": [
        "articles",
        "projects"
      ],
      "created_at": "2026-01-20T14:00:00Z",
      "last_used_at": null,
      "expires_at": null,
      "is_active": true
    }
  ]
}
DELETE/api/v1/api-keys/:id

Revoke an API key. Any application using it loses access immediately and the key cannot be restored. Session-authenticated.

Request
const keyId = 'b7e2f1a0-3c4d-4e5f-8a9b-0c1d2e3f4a5b';

const res = await fetch(
  `https://www.brainpercent.app/api/v1/api-keys/${keyId}`,
  {
    method: 'DELETE',
    credentials: 'include',
  }
);
const result = await res.json();
console.log(result.message);
200Response
{
  "success": true,
  "message": "API key revoked"
}

Error shape

These three routes return a flat { "error": "…" }. Bearer routes use the envelope. Branch on whether error is a string.

Permissions

PermissionAllowsExamples
readGETarticles, projects, credits
writePOSTgenerate, publish
deleteDELETENone of the Bearer routes. Revoke is session-only.

Scopes

ScopeRoutes
articles/api/v1/articles/*
social/api/v1/social/*
projects/api/v1/projects/*
user/api/v1/user/credits, /api/v1/user/usage

A request needs both the permission and the scope. Article generate needs write and articles.