Found while screening docs drift for #8287 (PR #8709). Pre-existing, unrelated to that change, and left unfixed there deliberately — correcting it is a docs-only job, not a rider on a code PR.
1. content/docs/references/identity/identity.mdx — the whole ApiKey table is the wrong schema
Its property list is better-auth's apiKey plugin schema:
| documented | reality on sys_api_key |
|---|
start, lastRefetchAt | do not exist |
rateLimitEnabled, rateLimitTimeWindow, rateLimitMax, remaining | do not exist — there is no rate-limit surface on this object at all |
permissions (Record<string, boolean>), metadata | do not exist |
enabled (boolean, required) | the column is revoked, with the opposite polarity |
organizationId — "Scoped organization ID" | did not exist until #8287, which adds active_organization_id (deliberately snake_case and deliberately notorganization_id — see that PR) |
better-auth's apiKey plugin is not loaded by this platform. plugin-auth/src/managed-extension-fields.ts states it outright: the table is hand-rolled ObjectStack, minted by runtime/src/domains/keys.ts and verified by core/src/security/api-key.ts. The real column set is name, prefix, user_id, active_organization_id, scopes, expires_at, last_used_at, revoked, key, id, created_at, updated_at.
This is the shape AGENTS.md PD #10 names — "never advertise or demo a capability the runtime doesn't actually deliver" — and the rate-limit rows are the sharpest instance: a reader can reasonably conclude API keys support per-key rate limiting, which nothing implements.
⚠️ Note for whoever fixes it: do not just add a row for the new column. The table's frame is wrong, so patching one line would make it more convincing and no more true.
2. content/docs/protocol/kernel/http-protocol.mdx — wrong key prefix in the example
GET /api/v1/data/taskX-API-Key: sk_live_abc123...
ObjectStack keys are osk_-prefixed (API_KEY_PREFIX in core/src/security/api-key.ts), and the prefix is load-bearing, not cosmetic: extractApiKey accepts Authorization: Bearer <token>only when the token starts with osk_, which is what stops a session Bearer being read as an API key. sk_live_… is a Stripe-shaped placeholder that would not authenticate and cannot be sent as a Bearer.
Screened and NOT affected (recorded so the next person need not re-check)
content/docs/automation/connectors.mdx, content/docs/references/integration/connector.mdx — a different subsystem: OUTBOUND connector auth (auth: { type: 'api-key', headerName: 'X-API-Key' }) for calling third-party APIs. Nothing to do with ObjectStack's inbound key surface.content/docs/api/index.mdx, content/docs/api/client-sdk.mdx, content/docs/ai/agents.mdx, content/docs/ai/connect-mcp.mdx, content/docs/getting-started/build-with-claude-code.mdx, content/docs/deployment/environment-variables.mdx — all accurate on the mint endpoint, the osk_ prefix and the header forms.content/docs/permissions/authentication.mdx, content/docs/permissions/system-context.mdx — screened, zero API-key vocabulary.content/docs/permissions/authorization.mdx — accurate, including its statement that sys_api_key stays public-posture with row scoping as its guard.
Found while screening docs drift for #8287 (PR #8709). Pre-existing, unrelated to that change, and left unfixed there deliberately — correcting it is a docs-only job, not a rider on a code PR.
1.
content/docs/references/identity/identity.mdx— the wholeApiKeytable is the wrong schemaIts property list is better-auth's
apiKeyplugin schema:sys_api_keystart,lastRefetchAtrateLimitEnabled,rateLimitTimeWindow,rateLimitMax,remainingpermissions(Record<string, boolean>),metadataenabled(boolean, required)revoked, with the opposite polarityorganizationId— "Scoped organization ID"active_organization_id(deliberately snake_case and deliberately notorganization_id— see that PR)better-auth's
apiKeyplugin is not loaded by this platform.plugin-auth/src/managed-extension-fields.tsstates it outright: the table is hand-rolled ObjectStack, minted byruntime/src/domains/keys.tsand verified bycore/src/security/api-key.ts. The real column set isname, prefix, user_id, active_organization_id, scopes, expires_at, last_used_at, revoked, key, id, created_at, updated_at.This is the shape AGENTS.md PD #10 names — "never advertise or demo a capability the runtime doesn't actually deliver" — and the rate-limit rows are the sharpest instance: a reader can reasonably conclude API keys support per-key rate limiting, which nothing implements.
2.
content/docs/protocol/kernel/http-protocol.mdx— wrong key prefix in the exampleObjectStack keys are
osk_-prefixed (API_KEY_PREFIXincore/src/security/api-key.ts), and the prefix is load-bearing, not cosmetic:extractApiKeyacceptsAuthorization: Bearer <token>only when the token starts withosk_, which is what stops a session Bearer being read as an API key.sk_live_…is a Stripe-shaped placeholder that would not authenticate and cannot be sent as a Bearer.Screened and NOT affected (recorded so the next person need not re-check)
content/docs/automation/connectors.mdx,content/docs/references/integration/connector.mdx— a different subsystem: OUTBOUND connector auth (auth: { type: 'api-key', headerName: 'X-API-Key' }) for calling third-party APIs. Nothing to do with ObjectStack's inbound key surface.content/docs/api/index.mdx,content/docs/api/client-sdk.mdx,content/docs/ai/agents.mdx,content/docs/ai/connect-mcp.mdx,content/docs/getting-started/build-with-claude-code.mdx,content/docs/deployment/environment-variables.mdx— all accurate on the mint endpoint, theosk_prefix and the header forms.content/docs/permissions/authentication.mdx,content/docs/permissions/system-context.mdx— screened, zero API-key vocabulary.content/docs/permissions/authorization.mdx— accurate, including its statement thatsys_api_keystays public-posture with row scoping as its guard.