Authentication
All API endpoints (except /api/v1/health) require authentication. NeuralRepo accepts a
session token, an API key, or a session cookie, and resolves them in a fixed order.
How a request is authenticated
Section titled “How a request is authenticated”Every /api/v1/* request runs through the same middleware chain before your endpoint sees it:
In prose, so nothing here lives only in the diagram:
- If an
Authorization: Bearerheader is present, its SHA-256 hash is looked up in the sessions table first, then in the api_keys table. That is why a bearer token can be either credential. - Otherwise an
X-API-Keyheader is tried, but only if it starts withnrp_— a key sent under that header without the prefix is ignored entirely. - Otherwise the
nrepo_sessioncookie is tried. - If none matches, the request is rejected with
401and the body{ "error": "Unauthorized" }. The message is the same whether the credential was missing, malformed, revoked, or expired. - A match sets the user and their plan; a session match also slides its expiry 30 days forward.
- The rate limiter then runs, but only for API-key and bearer-token requests.
Session Token
Section titled “Session Token”When you sign in through the NeuralRepo web app, a session token is stored in a secure HTTP-only cookie. You can also pass it explicitly:
Authorization: Bearer <session_token>Sessions last 30 days and slide: every authenticated request pushes the expiry another 30
days out, so an active session effectively never expires while a dormant one lapses after a
month. Signing out via /auth/logout deletes the row immediately.
API Key
Section titled “API Key”API keys provide long-lived access for scripts, CI pipelines, and third-party integrations.
Every key is the prefix nrp_ followed by 64 hexadecimal characters (32 random bytes):
X-API-Key: nrp_a1b2c3d4e5f6...Keys do not expire. Only the SHA-256 hash is stored, so a lost key cannot be recovered — only revoked and replaced.
Generating an API Key
Section titled “Generating an API Key”curl -X POST https://neuralrepo.com/api/v1/user/api-keys \ -H "Authorization: Bearer <session_token>" \ -H "Content-Type: application/json" \ -d '{"label": "CI Pipeline"}'const res = await fetch("https://neuralrepo.com/api/v1/user/api-keys", { method: "POST", headers: { Authorization: "Bearer <session_token>", "Content-Type": "application/json", }, body: JSON.stringify({ label: "CI Pipeline" }),});const key = await res.json();console.log(key);label is optional and defaults to default; it is capped at 100 characters.
Response 201 Created
{ "id": "3f2a91c47b0e4d5aa8c61e0f2b7d4c93", "key": "nrp_a1b2c3d4e5f67890...", "label": "CI Pipeline", "created_at": "2026-03-24 12:00:00"}The id is a 32-character hex string — it is what you pass to the delete endpoint, and it is
not the key.
Listing Keys
Section titled “Listing Keys”GET /api/v1/user/api-keysReturns every key on the account, newest first. The response carries no key material at all
— not a masked value, not a prefix. Each entry is id, label, scopes, source,
last_used_at, and created_at. source is manual for keys you created here and mcp for
keys minted by the MCP OAuth flow.
Revoking a Key
Section titled “Revoking a Key”DELETE /api/v1/user/api-keys/:idDeletes the row, so the key stops working on the next request. Returns 200 OK with
{ "success": true }, or 404 if the id does not belong to you. Unlike key creation, revoking
is available on every plan.
Scopes
Section titled “Scopes”Scopes are stored per key and are null for every key you create yourself, which means full access. Only tokens minted by the MCP OAuth flow carry a scope string:
| Scope | Granted to | Description |
|---|---|---|
ideas:read | MCP OAuth tokens | Read ideas, tags, relations, links, search, map, duplicates |
ideas:write | MCP OAuth tokens | Create, update, archive, merge, and develop ideas; manage tags, links, and relations |
A token whose scope string omits the scope an endpoint requires gets
403 { "error": "Insufficient scope: requires ideas:write" }. Session tokens and manually
created API keys never see that error, because they are unscoped.
MCP OAuth
Section titled “MCP OAuth”NeuralRepo issues MCP (Model Context Protocol) tokens for AI assistant integrations. The flow is separate from standard API authentication and is driven by the client, not by you:
- You add
https://neuralrepo.com/mcp/as an MCP server in a compatible client. - The client discovers the endpoints from
/.well-known/oauth-authorization-server, registers itself, and walks the authorization-code + PKCE flow at/mcp/authorizeand/mcp/token. - The token it receives is written to the same
api_keystable withsource = "mcp", a label ofMCP (<client>), and the scopes you approved.
GET /api/v1/user/mcp-tokens lists the tokens issued this way — it does not create one.
Revoke them with the same DELETE /api/v1/user/api-keys/:id endpoint used for manual keys.
Error Responses
Section titled “Error Responses”| Status | Body | Meaning |
|---|---|---|
401 Unauthorized | { "error": "Unauthorized" } | Missing, malformed, revoked, or expired credential |
403 Forbidden | { "error": "Insufficient scope: requires ideas:write" } | Valid MCP token, wrong scope |
403 Forbidden | { "error": "API access requires a Pro plan", "pro_required": true, ... } | Valid credential, plan gate |
There is no distinct “invalid API key” message — every authentication failure returns the same
401 body, by design.
Best Practices
Section titled “Best Practices”- Use API keys for server-side scripts and automations.
- Use session tokens only from browser-based code.
- Rotate API keys periodically and revoke unused keys —
last_used_aton the list endpoint tells you which are dormant. - Never commit API keys to version control.