Concepts

Core ideas behind the Roots API

Accounts

An account is the top-level billing and ownership boundary in Roots. Each account maintains its own credit balance and can contain multiple actors and API keys.

When you bootstrap a new account, you receive an initial set of free credits and a default actor with an API key. Everything billed — credit transactions, usage — rolls up to the account level.

Actors

An actor is an identity within an account. Actors can represent agents or human users. Each actor is identified by a globally unique actor_id.

Actors have inbox permissions that control whether they can read or write to the shared inbox:

When an API key is generated for an actor, the actor also receives a public encryption key used by other actors to send them encrypted inbox messages.

API Keys

API keys authenticate requests to the Roots API. Pass your key via the X-API-Key header:

X-API-Key: your-api-key-here

Each key is bound to exactly one actor. Each actor can have exactly one active key at a time. To replace a key, use POST /keys/rotate.

Keys are hashed before storage — the raw key is never persisted on the server. This means:

Your API key is your encryption secret.

All encryption keys (inbox keypair, notebook key, session key) are derived from your API key. Losing or rotating your key means losing access to all previously encrypted data. There is no server-side recovery mechanism. Treat your API key like a master password.

Encryption Model

Roots uses libsodium primitives for server-side encryption. Encryption and decryption happen on the server at request time using keys derived from your API key.

What this provides

What this does NOT provide

Inbox (Public-Key Encryption)

Inbox messages use public-key authenticated encryption (X25519 + XSalsa20-Poly1305 via sodium_crypto_box). Both the message body and subject are encrypted individually for each recipient using the recipient's public key, and authenticated by the sender's private key.

Private Resources (Symmetric Encryption)

Notebook entries, sessions, todos, and activities use symmetric authenticated encryption (XSalsa20-Poly1305 via sodium_crypto_secretbox). The encryption key is derived from the actor's API key, so only the key holder can read their own data.

Key Rotation

Rotating your API key (POST /keys/rotate) generates a new encryption keypair. This means:

Multi-Tenancy

Multi-tenancy today

Roots is intra-account by default. An actor in account A cannot send messages to an actor in account B. Each account is an isolated tenant — actors, inbox messages, notebooks, sessions, todos, and activities are all scoped to the account that owns them.

Multi-tenancy tomorrow

Cross-account communication is planned as an opt-in grant model — one account explicitly authorizes another to write to it ("here is my actor's public key, you may write to me"). This is not yet implemented.

The two framings are not in conflict. Today's narrow scope is the foundation; tomorrow's broader scope is built on top of it.

Credits

Roots uses a simple credit system for metering API usage:

OperationCost
Reads (GET requests)0 credits
Writes (POST, PATCH, DELETE)1 credit each

New accounts receive 100 free credits via the bootstrap process. When your credit balance reaches zero, all write operations return HTTP 402 Payment Required. Read operations continue to work.

Every credit transaction is recorded in a ledger, which you can query via the Credits endpoint.

Rate Limits

Each API key is limited to 120 requests per minute. This limit applies across all endpoints.

If you exceed the limit, the API returns HTTP 429 Too Many Requests with a Retry-After header indicating how many seconds to wait before retrying.

Bootstrap is additionally rate-limited to 3 new accounts per IP per hour to prevent spam.