Core concepts

The two planes, organizations, apps and users, uid addressing, roles and policies, handlers and agents.

A handful of ideas explain how Authoric is arranged. Every other page assumes them.

Two planes

Authoric keeps two kinds of database apart, and the API is shaped by the split.

  • The platform plane is your Authoric account: your organization, its members, its subscription, and the apps it owns. You work in it through the console. The console's own API is not part of the published contract and is not something your application calls.
  • The app plane is each app's own database: its users, tables, files, roles, policies, handlers and agents. This is what your application calls at runtime, under /{appUid}/…, and it is what the API reference documents.

A caller on an app's plane can reach that app and nothing else. Naming another app, or the platform, is refused (CROSS_PLANE_FORBIDDEN) before anything is read, so a token minted for one app's user can never create a table, change a policy, or read a neighbor's data.

Organizations, apps and users

ThingLives onWhat it is
Your organizationPlatformYour customer account. Owns apps, holds the subscription, has members.
MembersPlatformThe people on your team who sign in to the console.
AppPlatform registry, own databaseOne isolated database with its own users, tables, policies, handlers, storage and agents.
App usersAppThe end users of your product. They sign up and sign in to your app, never to the console.
App organizationsAppYour product's own tenants, inside the app. Every app user belongs to at least one.

Your organization and your app's organizations are unrelated. The second set is the multi-tenancy your product offers its own customers: when someone signs up to your app they get an organization of their own, and rows they create in an org-scoped table belong to it. Inside the app, a caller sees only its own organization's rows; another organization's row is a 404, the same as a missing one. You, the app's owner, see every organization's rows from the console. See Organizations.

Isolation between apps is physical: each app is its own database. An app is created first and provisioned later, which is when the database is built; console sections that need it stay disabled until then.

Records are addressed by uid

Every record that leaves the database is named by its uid, a UUID. The sequential id is the database's internal key: it never appears in a URL, a request, a response, an event, an SDK call or a tool schema.

  • A row is read, updated and deleted at /{appUid}/autocrud/{table}/{rowUid}.
  • A reference to another row is sent and returned as <name>_uid. A foreign key column customer_id appears in the API as customer_uid, holding the customer row's uid.
  • Sending id, uid, org_id or a raw foreign key column in a body is a 400 (ROW_FIELD_NOT_ACCEPTED) whose message names the field to use instead.
  • Uids carry a creation timestamp. Don't treat one as secret, and sort by created_at (or id in sort), never by uid.

Principals, roles and policies

A principal is whoever is making a request. In an app they are:

PrincipalHow it callsTypical use
App userAn access token from sign-inYour product's runtime traffic
AgentAn API key bound to that one agent, or a console runAI that reads and acts on the app's data
Scheduled jobRuns as its own principal on a clockBackground work
You, the ownerThe consoleBuilding and operating the app

Every one of them is authorized the same way. A principal holds roles; roles carry policies; a policy grants actions on resources, such as notes:read_list or refunds:create, optionally under conditions (an amount ceiling, ownership of the row). Anything not granted is denied. The check runs per request against the caller's own roles, so a token can never widen its own reach. Your app's users start with the built-in User role, which grants membership basics and no access to your tables until you add it.

Work done for the owner without a person clicking, such as the owner's agents, scheduled jobs and API keys, has an org reach: which of the app's organizations it may act in. The default is none beyond the owner's own records.

Read The governance model for how this extends to agents, and Access control for the console steps.

Handlers

A handler is server-side code you write for an app. It runs when an endpoint is called, on a schedule, or as a tool an agent calls, and it reaches the database, storage, email and AI through an SDK (sdk.*). Each SDK call is a capability (sdk:read, sdk:write, sdk:query, …) checked against the principal the handler runs for, so a handler cannot do more than its caller may. See Handlers and Scheduled jobs.

Agents

An agent is a principal in an app with an AI model behind it. It has no tool list of its own: its tools are derived from the policies on its roles, and the same derivation serves its runs, direct tool calls and MCP clients. Steps you mark as needing a person pause the run until someone approves or rejects them. Your application reaches an agent over REST (/{appUid}/agent/…) or MCP (/{appUid}/mcp) with the agent's API key. See Agents & AI.

The example app

Your app's API is shaped by the tables you create, so these docs need some tables to show. They use a sample invoicing app with five: vendors, invoices (each referencing a vendor), payments, purchase_orders and approval_thresholds. It is the app the API reference is generated from, which is why an example such as /{appUid}/autocrud/invoices links straight to its operation page.

No app has these tables by default. Yours has only the tables you create; the quickstart makes one called notes. Every table gets the same five operations and the same rules, so an example carries over unchanged once you put your own table and column names in it. Where a page describes a path in general, it writes the table as {table}.

What's next

On this page