Core Concepts
Projects
A project is the sub-scope inside a tenant. Memories live in projects; queries and writes target one project at a time. This page covers the model, how the project id is resolved on every request, and the difference between header-driven and key-pinned scoping.
Project model fields
| Field | Notes |
|---|---|
project_id | String, unique, indexed. Format: "proj_" + 16 hex characters. |
tenant_id | FK to Tenant.tenant_id. Hard ownership. |
name | Human label. |
is_default | True for the auto-created project on a fresh tenant. |
memory_count | Maintained counter. |
The default project
Every tenant starts with one project marked is_default=true. If a request omits a project context entirely, the default project is used — but only on routes that do not enforce a project scope. Project-scoped routes still require an explicit signal.
How <code>project_id</code> is resolved on a request
- 1The middleware reads the
X-Project-IDheader, validates its format, and checks ownership against the resolved tenant. - 2If the header is missing, the middleware checks whether the active API key is project-locked (its
project_idcolumn is set). - 3If neither source produces a project, the project-enforcement dependency raises 400 on routes that require one.
- 4If both produce a project, the header takes precedence — but ownership is still checked.
Project-locked API keys
When you create an API key with POST /org/api-keys you can set project_id. A key with a project_id satisfies project-scope enforcement automatically — callers can omit X-Project-ID on every request.
Use it for
One service, one project. Cleaner request shape, fewer chances of header drift.
How memories link to projects
Memory.project_idis a nullable FK toProject.project_idwithON DELETE SET NULL.- Deleting a project does not delete its memories — they go org-wide (
project_id = null) until you re-assign them. Project.memory_countis maintained by the platform; you do not write to it.
CRUD routes
| Route | Action |
|---|---|
GET /org/projects | List projects in the active tenant. |
POST /org/projects | Create a new project (allocates a fresh project_id). |
PATCH /org/projects/{project_id} | Update name or default flag. |
DELETE /org/projects/{project_id} | Soft-delete; memories drop to org-wide. |
What projects do not do
- Projects do not provide isolation strong enough to substitute for tenants — cross-project access inside a single tenant is allowed by the model.
- Projects do not bill independently — quota and usage count at the tenant level.
- Projects do not have nested children.