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.
| Method | Path | Auth | What |
|---|---|---|---|
| POST | /api/v1/api-keys | Session | Create |
| GET | /api/v1/api-keys | Session | List |
| DELETE | /api/v1/api-keys/:id | Session | Revoke |
/api/v1/api-keysCreate 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
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Human-readable label for the key (1-100 characters) |
| permissions | string[] | Optional | Permissions to grant: read, write, delete Default ["read"]. |
| scopes | string[] | Optional | Resource scopes to restrict access: articles, social, projects, user |
| expires_in_days | integer | Optional | Key expiration in days (1-365). Omit for no expiration. |
// 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);{
"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"
}
}/api/v1/api-keysList the API keys on your account. Returns masked key prefixes only, never the full key. Session-authenticated.
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}`));{
"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
}
]
}/api/v1/api-keys/:idRevoke an API key. Any application using it loses access immediately and the key cannot be restored. Session-authenticated.
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);{
"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
| Permission | Allows | Examples |
|---|---|---|
| read | GET | articles, projects, credits |
| write | POST | generate, publish |
| delete | DELETE | None of the Bearer routes. Revoke is session-only. |
Scopes
| Scope | Routes |
|---|---|
| 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.