DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS /// DOCS
TABLES·REFERENCES

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 fieldInline reference
Where it livesA dedicated REFERENCE-type columnInside a LONGTEXT cell (or any other rich-text surface)
How you add itPick from the column's popover editorType @ in the editor, pick from the picker
Wire formatArray of IDs stored in records.fields[label](ref:entity_type:id) embedded in the cell's string value
What it can point atAny entity type (configured per column)Any entity type
Multiple targetsUp to max (configurable, default 1)As many inline pills as you want
Onnie AI follows itYesYes

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 uses entity_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_typeWhat it points at
tableA table in the Tables module
pageA page or subpage
folderA page-tree folder
routineA scheduled automation
functionA workspace-defined function
taskA task (engine_tasks)
recordA single row inside a table
projectA project
userA workspace member
teamA workspace team
engine_botA custom or built-in bot (Superagent, etc.)
skillA saved skill
variableA workspace-scoped variable
//WATCH OUT

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 id column, a standard UUID like 5f9b2a01-9c4d-4b78-9e7f-3a8c1d6b0e2a.
  • alias_id — a 12-character base64 token like NWY5YjJhMDE_, auto-generated by the set_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.

//NOTE

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.