References
References let any part of your workspace point at any other part — a record in a table, a page, a bot, a team member, a Task. There are two ways a reference can live in a table: as a dedicated Reference field on a column, or as an inline reference embedded in a LONGTEXT cell or other rich-text surface.
Both forms share the same anchor: the entity's UUID. When the target is renamed, the displayed label updates automatically everywhere it appears.
Two kinds of references
| Reference field | Inline reference | |
|---|---|---|
| Where it lives | A dedicated REFERENCE-type column | Inside a LONGTEXT cell (or any other rich-text surface) |
| How you add it | Pick from the column's popover editor | Type @ in the editor, pick from the picker |
| Wire format | Array of IDs stored in records.fields | [label](ref:entity_type:id) embedded in the cell's string value |
| What it can point at | Any entity type (configured per column) | Any entity type |
| Multiple targets | Up to max (configurable, default 1) | As many inline pills as you want |
| Onnie AI follows it | Yes | Yes |
Reference field type
A REFERENCE-type column stores one or more links to other entities. You configure:
- entity_type — which kind of entity the column points at. This determines what the picker searches. For example, a column pointing at another table's records uses
entity_type: 'record'; a column pointing at pages usesentity_type: 'page'. - max — optional cap on how many references the cell can hold (default: no cap).
The cell displays the resolved labels for each referenced entity, fetched live. The inline editor is a popover with a live search against the configured entity type.
If you're pointing one table's records at another table's records (a classic "foreign key" relationship), set entity_type to record and configure which table the picker searches. The cell will show the record's display name and clicking it will open the record.
Filter operators for REFERENCE columns: isEmpty, isNotEmpty, contains, is, isNot.
Inline references in text
LONGTEXT cells, page bodies, task bodies, chat messages, and other rich-text surfaces support inline references. Plain TEXT cells use a basic single-line input and do not support inline references. They are created by typing @ in the editor and selecting an entity from the picker. The result is a pill — a clickable badge that opens the entity.
Wire format
On disk (and in the markdown mirror), inline references use this format:
[Display Name](ref:entity_type:id)
[Display Name](ref:entity_type:id/alias_id)
Examples:
[Contacts](ref:table:5f9b2a01-9c4d-4b78-9e7f-3a8c1d6b0e2a/NWY5YjJhMDE_)
[Alex Rivera](ref:record:9c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f)
[Daily Briefing](ref:routine:7a1c3d4e-2b6f-4a8c-9d1e-0c5b7f3a2e1d/N2ExYzNkNGU_)
[Superagent](ref:engine_bot:00000000-0000-0000-0000-000000000001)
Do not invent other syntaxes. @[label](onnie://...), {ref:Contacts}, and similar variants are not recognized by the parser.
Valid entity types
The entity_type part of the reference must be one of the registered types:
entity_type | What it points at |
|---|---|
table | A table in the Tables module |
page | A page or subpage |
folder | A page-tree folder |
routine | A scheduled automation |
function | A workspace-defined function |
task | A task (engine_tasks) |
record | A single row inside a table |
project | A project |
user | A workspace member |
team | A workspace team |
engine_bot | A custom or built-in bot (Superagent, etc.) |
skill | A saved skill |
variable | A workspace-scoped variable |
When referencing a bot, use engine_bot — not bot. The type bot is not registered and will not render as a pill.
How the editor inserts them
When you type @ in any rich-text surface, a picker opens and searches all entity types simultaneously. Selecting an entity from the picker inserts a reference node in the editor's JSON document tree and a [label](ref:type:id) link in the markdown mirror. Both representations are kept in sync at every save.
How Onnie AI follows them
When Onnie AI reads a text surface (a task body, a page, a cell value) it parses inline references and follows them to their targets. A task body that contains [Q2 Pipeline](ref:table:...) tells the Onnie AI it can look up the contents of that table as context. References are a key mechanism for giving the Onnie AI structured context without copying data manually.
Alias IDs vs UUIDs
Entities that support aliases have two ways to address them:
- UUID — the canonical
idcolumn, a standard UUID like5f9b2a01-9c4d-4b78-9e7f-3a8c1d6b0e2a. - alias_id — a 12-character base64 token like
NWY5YjJhMDE_, auto-generated by theset_alias_id()database trigger when the row is inserted.
The alias_id tail in the reference URL is optional. When present, it allows shorter URLs and human-readable links in some contexts. Both forms resolve to the same entity.
-- The trigger that generates alias_id:
if coalesce(trim(new.alias_id), '') = '' then
new.alias_id := replace(
substring(encode(new.id::text::bytea, 'base64'), 1, 12),
'/', '_'
);
end if;
The trigger only sets alias_id if it isn't already populated, so you can override it manually by inserting a row with an explicit alias_id value.
The alias_id is derived from the UUID bytes, not from the entity's name. It is not a slug. Never write a reference like (ref:table:<uuid>/contacts) — that's a made-up alias, not a real one. Always read the alias_id from the row returned after insert; never construct it yourself.
Not all entities have an alias_id. Records, users, teams, tasks, and variables do not have aliases and are always referenced by UUID alone:
[Alex Rivera](ref:record:9c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f)
Tables, pages, routines, functions, engine_bots, skills, projects, and folders do have aliases and can use either form.
When references break
When the target of a reference is deleted:
- Inline references — the pill falls back to rendering as plain bracketed text:
[Display Name]. The underlying(ref:type:id)syntax stays in the stored markdown. If the entity is later restored (or a new entity has the same UUID), the pill resolves again. - Reference fields — the cell falls back to showing the raw ID string(s) instead of the resolved label. The ID is still stored; it just can't be resolved.
Neither case throws an error or blocks saving. The reference becomes a dead link rather than crashing the renderer.
Onnie AI treats unresolvable references as absent context — it won't error on them, but it also won't try to fabricate what the deleted entity contained.