Appearance
Authentication
OpenBrain SaaS uses Laravel Sanctum personal access tokens for the REST API and MCP (/mcp).
Token abilities
| Ability | REST | MCP tools |
|---|---|---|
memory:ingest | POST /api/memories, PUT /api/memories, POST /api/memories/batch | memory_ingest, memory_upsert, memory_ingest_batch |
memory:search | POST /api/memories/search, GET /api/memories/{id} | memory_search, memory_get, memory_recent, memory_stats, memory_changes |
memory:admin | PATCH /api/memories/{id}, DELETE /api/memories/{id} | memory_update, memory_delete |
organization:{uuid} | Binds the token to one tenant | Same |
The wildcard ability * grants all scopes (intended for local development only).
Organization context
- Prefer embedding the org in the token:
organization:{uuid}. - Optionally send
X-Organization-Id— REST and MCP both honor it. The header must match the token’sorganization:ability when both are present. Unknown and unauthorized organizations return the same 403. - A user principal must have a live membership in the resolved organization, even when the token is org-scoped. Tokens without an
organization:ability resolve only when the user belongs to exactly one organization. - Search-only tokens cannot call
memory_upsertormemory_ingest_batch. Those tools requirememory:ingest.
Creating tokens
php
$user->createToken('agent-ingest', [
'organization:'.$organizationId,
'memory:ingest',
'memory:search',
]);Issue separate tokens for search-only automation vs ingest-heavy agents vs admin (update/delete) workflows.
Organization roles (OBS-106)
| Role | Dashboard (/app) | API keys | Billing |
|---|---|---|---|
owner | Full tenant access | Create tokens with any abilities | Yes |
member | Memories + API keys | Create tokens (ability checkboxes) | No |
readonly | View/search UI | Create search-only tokens | No |
Owners invite teammates from the Team page. Invitees receive a one-time email link to /invitations/accept/{token} (email must match; the link expires). Owners can view and revoke every API key scoped to the current organization; other members see and can revoke only their own.
Audit log (OB-047)
Successful ingest, upsert, update, delete, and batch operations write org-scoped rows to memory_audit_events (actor user/token, action, thought id, metadata such as outcome/dedupe — never raw content). Owners can review events in the dashboard Audit log.
REST requests
http
Authorization: Bearer {token}
X-Organization-Id: {uuid} # optional when token includes organization:{uuid}
Content-Type: application/jsonMCP
- Production: Sanctum Bearer token required on every
/mcprequest. MCP OAuth is unsupported. - Organization header: MCP reads
X-Organization-Idfrom the HTTP request. Resource URIs also select an organization; a header and URI that disagree are denied. - Local: Unauthenticated MCP is allowed only when
APP_ENV=localandOPENBRAIN_DEFAULT_ORGANIZATION_IDis set. Resource URIs (brain://{organizationId}/…) may only use that configured organization ID; other organization IDs are denied.
Billing on writes
When OPENBRAIN_BILLING_ENFORCED=true and Stripe is configured, writes (ingest, upsert, batch, update, delete) require an active subscription or trial and return 402 with code: "entitlement_required" (MCP: tool error) when unpaid. Reads (search, get, recent, export, stats, changes, MCP read tools and resources) stay available. Metered reads (search, export, stats, changes, recent) still count toward the search quota; get does not.
If enforcement is on but STRIPE_SECRET is empty, writes fail closed with 503 and code: "service_unavailable" rather than being allowed. Metered operations also return 503 when the organization's subscription price is not one this deployment recognizes, because no plan quota can be applied. See Billing and usage for per-plan caps.
Rate limits
Memory operations are limited per resolved organization + credential (Sanctum PAT ID, authenticated session user, or system principal), with a 60-second decay. Limits apply consistently to REST, MCP, dashboard, and webhook operations:
| Variable | Default | Applies to |
|---|---|---|
OPENBRAIN_RATE_LIMIT_INGEST | 60 | ingest, upsert, batch |
OPENBRAIN_RATE_LIMIT_SEARCH | 120 | search, export, stats, changes, recent, get |
OPENBRAIN_RATE_LIMIT_ADMIN | 30 | update, delete |
Exceeded limits return HTTP 429 with code: "rate_limited" and Retry-After.