Browse the docs

Authentication and keys

Agent keys, scope limits, strict mode, and what a key can reach.

The Product API authenticates with an agent key. Browser sessions are not accepted.

There are two ways to get one:

  • Connect — the product sends the workspace owner to Buddhi, they approve, and the product receives its credentials directly. Nobody copies a key. See Connect a product.
  • Create a key by hand — described below. Fine for your own integrations and for testing.

Create a key

  1. In Buddhi, open Gateway & Agents.
  2. Create a new agent key and name it after the product, for example Burrito AI. Memories the key writes show this name as their source.
  3. Set the surface to API (another product).
  4. Under Limit this key to scopes, tick only the scopes the product should use.
  5. Leave Strict mode on, and choose an expiry if you want one.
  6. Copy the key (crtx_…) and, for a strict key, the signing secret (bss_…). Each is shown once.

Treat both values like passwords. Keep them in server-side environment variables and never ship them to a browser or mobile app. If either leaks, revoke the key in Gateway & Agents; it stops working immediately.

What a key can do

A key acts as the person who issued it. It can never do more than that person can.

A key limited to scopes is narrower still:

Unlimited keyLimited key
ReadsEverything its issuer can readOnly the listed scopes
WritesEverything its issuer can writeOnly the listed scopes its issuer can write
Issuer's personal scope (their Gmail and Drive)IncludedNever included
Overruling an existing memoryAnywhere its issuer may writeOnly inside the listed scopes

The last row matters for generated content. If a marketing-only key writes something that contradicts an engineering memory, the engineering memory is not replaced. Both stay in use and the workspace owner decides between them in Conflict Resolution.

For a product that publishes content, limit the key to the Publishable scope. It exists for exactly this: knowledge the company has cleared to appear in public. A whole marketing scope also holds plans, budgets and unannounced launches.

You can only give a key scopes you have yourself. Asking for others returns 403.

Writing: review by default

What a key writes is held for a person to approve unless the workspace owner ticked Write without review when issuing it. Reading is unaffected. A product that only fetches context needs nothing more; one that records published content back into Buddhi needs a trusted key, or its writes wait in the Agent Memory inbox. See Agent memory.

Plain keys and strict keys

Plain keyStrict key
Sent on every requestYes, as the bearerNo — only to the token exchange
Data calls carryThe keyA 10-minute access token
Requests are signedNoYes
Works on the MCP gateway and CLIYesNo
Use it forAI agents, quick testsAnother product's server

A strict key protects against the two things that go wrong with long-lived credentials: the key is almost never on the wire, and a captured request cannot be changed or replayed.

1. Exchange the key for an access token

POST /api/v1/token

Send the key as the bearer and sign the request (below). There is no body.

{ "access_token": "bat_…", "token_type": "Bearer", "expires_in": 600 }

Use access_token as the bearer on every other call. When it expires, or a call returns 401 invalid_token, exchange again. Revoking the key cuts off its outstanding tokens at once.

2. Sign every request

Add two headers:

X-Buddhi-Timestamp: 1791234567890
X-Buddhi-Signature: v1=<hex>
  • Timestamp: the current Unix time in milliseconds. It must be within five minutes of Buddhi's clock.
  • Signature: v1= followed by the hex HMAC-SHA256, keyed with the signing secret, of this string:
<timestamp>.<METHOD>.<path and query>.<sha256 hex of the body>

For a GET, the body is the empty string. The path is exactly what you request, for example /api/v1/context.

import { createHash, createHmac } from "node:crypto";

function sign(secret: string, method: string, path: string, body = "") {
  const timestamp = String(Date.now());
  const bodyHash = createHash("sha256").update(body).digest("hex");
  const signature = createHmac("sha256", secret)
    .update(`${timestamp}.${method.toUpperCase()}.${path}.${bodyHash}`)
    .digest("hex");
  return { "X-Buddhi-Timestamp": timestamp, "X-Buddhi-Signature": `v1=${signature}` };
}

Sign the exact bytes you send. Serialise the JSON body once and use that same string for both the signature and the request.

Each signature is accepted once. Sending the identical request again with the same timestamp returns 401 replayed_request; a retry needs a fresh timestamp.

If you may send two identical requests in the same millisecond, add X-Buddhi-Nonce with a random value of up to 64 URL-safe characters and append .<nonce> to the string you sign. The drop-in client does this for every request.

Say who is asking

Send the person who triggered the call in your product:

X-Buddhi-End-User: priya@yourcompany.com

Buddhi records it in the audit log and shows it in the key's activity, so "what did the product ask, and for whom" has an answer. It is for attribution only: it never changes what the key can reach. Up to 200 characters.

Check a key

GET /api/v1/me

Confirms the credentials work and shows what they reach. Make this the first call of any integration.

{
  "key": {
    "id": "0d3c…",
    "name": "Burrito AI",
    "limitedToScopes": ["publishable"]
  },
  "actingAs": { "name": "Vansh", "role": "CEO" },
  "scopes": {
    "read": ["publishable"],
    "write": ["publishable"],
    "includesPersonal": false
  }
}
FieldDescription
key.limitedToScopesThe key's scope limit, or null for an unlimited key
actingAsThe person the key acts for
scopes.readScopes recall, context and facts draw from
scopes.writeScopes the key may add memories to
scopes.includesPersonalWhether the issuer's personal scope is reachable. Always false for a limited key

Expiry

A key can be given an expiry of 30 days, 90 days or a year when it is created. After that it stops working and is shown as expired. Issue a new key before then; there is no way to extend one.

See what a key has been doing

In Gateway & Agents, open Activity on a key. It lists each context read, memory written, fact lookup and token exchange, with the time, the scope, whether it was allowed, and the end user the product reported. The key's owner and the workspace owner can see it.

Revoke a key

Revoke it in Gateway & Agents. The next request returns 401 invalid_key, including requests carrying a token that was issued from it.