Work with the Database module from the terminal: records, schemas (collections), schema triggers, database integrations, taxonomies and their terms, test data. The routes a developer does not run from a terminal are listed at the end, with the reason.
Every command is covered by tests/db-routes.test.ts, which runs it through the
real SDK with fetch replaced and checks the verb and path it sends (so a wrong
route token fails a test), and the request body for the commands that build
one. No test contacts a database.
All paths below are under /{version}/database. Record commands (find, get,
count, distinct, insert, insert-many, update, replace, delete,
aggregate, change-owner, terms) call the data-plane API; the rest call the
Hub. Both need only the project API key.
JSON flags take inline JSON, @file.json, or - for stdin (the older record
commands — insert, update, replace, delete, find — take inline JSON or
-). Flags called --file / --ui-file take a path, or - for stdin.
Many commands take --integration <id> to use a database integration other
than the project default.
norbix module enable database / norbix module disable database turn the
module on and off (PUT /enable, PUT /disable). A gateway older than
refactoringV2 (2026-10) has only the GET routes, so this CLI needs a current gateway.
| command | what it does | endpoint |
|---|---|---|
norbix db find <collection> [--filter <json>] [--page-size <n>] [--after <cursor>] [--before <cursor>] [--expand] |
find records | GET /collections/{collectionName} |
norbix db get <collection> <id> [--expand] |
one record | GET /collections/{collectionName}/{id} |
norbix db count <collection> [--filter <json>] |
count records | GET /collections/{collectionName}/count |
norbix db distinct <collection> <field> |
distinct values of one field | GET /collections/{collectionName}/distinct |
norbix db insert <collection> --doc <json> |
insert one record | POST /collections/{collectionName} |
norbix db insert-many <collection> --docs <json-array> |
insert many | POST /collections/{collectionName}/many |
norbix db update <collection> --id <id> --update <json> [--array-filters <json-array>] |
update one | PUT /collections/{collectionName}/{id} |
norbix db update <collection> --many --filter <json> --update <json> [--array-filters <json-array>] [--yes] |
update many | PUT /collections/{collectionName}/many |
norbix db update <collection> --all --update <json> [--array-filters <json-array>] [--yes] |
update every record | PUT /collections/{collectionName}/many (allRecords: true) |
norbix db replace <collection> --id <id> --doc <json> |
replace one record fully | PUT /collections/{collectionName}/{id}/replace |
norbix db delete <collection> --id <id> [--yes] |
delete one | DELETE /collections/{collectionName}/{id} |
norbix db delete <collection> --many --filter <json> [--yes] |
delete many | DELETE /collections/{collectionName}/many |
norbix db delete <collection> --all [--yes] |
delete every record | DELETE /collections/{collectionName}/many (allRecords: true) |
norbix db aggregate <collection> --pipeline <json> |
run an inline aggregation | POST /collections/{collectionName}/aggregate |
norbix db aggregate <collection> --id <aggregateId> [--tokens <json>] |
run a saved aggregate | POST /collections/{collectionName}/aggregates/{aggregateId}/execute |
norbix db aggregates [--schema <id>] |
list saved aggregates | GET /aggregates |
norbix db change-owner <collection> <id> --user <userId> |
hand a record to another user | PUT /collections/{collectionName}/{id}/responsibility |
The record's owner (responsible user) decides who may read or change it under
"own" permissions. change-owner is the only way to move a record to another
user: an update or a replace does not change the owner. The new owner must be a
user of the project in the same environment (CM-ERRORS-MEMBERSHIP-USERS-012
otherwise).
Rules for record writes:
--updateis the plain fields to change, e.g.'{"status":"paid"}'. The gateway applies them with$set. An operator ($set,$inc, …) is refused (CM-ERRORS-DATABASE-035); the CLI refuses it before sending.- An empty filter (
--filter '{}') would touch every record, soupdate --manyanddelete --manyrefuse it (CM-ERRORS-DATABASE-037). To change or delete the whole collection on purpose, use--all: it sendsallRecords: trueand always asks first (--yesskips the question). - A user with only "own" rights (create as user, update own, delete own) may
run
insert-many,update --manyanddelete --many; they touch only that user's own records. - A broken
insert,insert-manyorreplacedocument answersCM-ERRORS-DATABASE-036("Invalid record document", with the item index forinsert-many). - A soft-deleted record is "not found" for
update,replaceandchange-owner;update --manyskips it.
A reference field (user, role, taxonomy term, record of another collection,
file) stores an id. db find --expand and db get --expand send
expandReferences: true: every reference then reads as {id, display} —
display is the value of the field the schema names as displayField on the
target (a role shows its name, a file its name), null when the target is
gone; a list of them on a field that holds several ids. Without --expand the
stored ids come back unchanged.
The caller needs read rights on the collection and on every source the
published schema links to; a missing one refuses the whole read with
CM-ERRORS-DATABASE-056, naming the source — a permission gap never looks
like missing data. A file id from an expanded reference opens with
norbix files get-by-id <id>.
norbix db find articles --expand --json | jq '.list.items[] | {title, author: .author.display}'
norbix db get articles 66b2f0a1c3d4e5f6a7b8c9d0 --expandA record is stored as the JSON you send: objects and lists of objects stay
nested at any depth, and the schema is checked at every level (a broken value
names the full path, e.g. lines[1].qty). An --update key may be a nested
path:
| key | changes |
|---|---|
"address.city" |
one field inside an object |
"lines.0.qty" |
one list element by position |
"lines.$[line].qty" |
the list elements --array-filters picks — a JSON array with one filter object per $[name] |
--array-filters works with --id, --many and --all. The CLI refuses a
value that is not a JSON array of objects before sending; the gateway refuses
a $[name] without its filter, or a filter without its $[name].
norbix db update orders --id 66b2f0a1c3d4e5f6a7b8c9d0 \
--update '{"lines.$[line].qty":3,"meta.words":130}' \
--array-filters '[{"line.sku":"A-1"}]'| command | what it does | endpoint |
|---|---|---|
norbix db indexes <collection> |
list the indexes of a collection | GET /collections/{collectionName}/indexes |
norbix db seed --collections <json-array> [--mode dummy|realistic] [--yes] |
fill several collections with test records | POST /collections/seed |
seed reads the published schemas, inserts parents before children and puts
real record ids into reference fields, so you never invent an id. It is all or
nothing: every document is validated first. It asks before it writes (--yes
skips the question; without a terminal and without --yes it exits 3).
# dummy (default): the server makes up schema-valid records, up to 100 per collection
norbix db seed --collections '[{"collectionName":"families","count":3},{"collectionName":"pairs","count":5}]' --yes
# realistic: you give the documents; a reference to another seeded record is a $seedRef
cat > seed.json <<'JSON'
[
{"collectionName": "families", "documents": [{"name": "Rosaceae"}]},
{"collectionName": "pairs", "documents": [{"family": {"$seedRef": {"collection": "families", "index": 0}}}]}
]
JSON
norbix db seed --mode realistic --collections @seed.json --yesThe answer is a report per collection: insert order, requested and inserted counts, the new ids, and any errors.
| command | what it does | endpoint |
|---|---|---|
norbix db schemas [--page-size <n>] [--after <cursor>] |
list the schemas of one environment (--env, default PROD); each row carries env |
GET /schemas |
norbix db schema <id> |
show one schema | GET /schemas/{id} |
norbix db schema rules [--json] |
print the schema design rules (which field for which value, with the exact JSON shape); sends nothing | — |
norbix db schema create --name <name> --file <data-schema.json> [--ui-file <ui.json>] [--settings <json>] |
create a schema; prints the Hub's design warnings on stderr |
POST /schemas |
norbix db schema update <id> [--file <data-schema.json>] [--ui-file <ui.json>] |
save a change as the draft | PUT /schemas/{Id}/draft |
norbix db schema draft <id> |
show the unpublished draft | GET /schemas/{Id}/draft |
norbix db schema discard <id> [--yes] |
throw the draft away | DELETE /schemas/{Id}/draft |
norbix db schema publish <id> [--yes] |
publish the draft | POST /schemas/{Id}/publish |
norbix db schema versions <id> |
list the published versions | GET /schemas/{Id}/versions |
norbix db schema diff <id> --from <n> --to <n> |
what changed between two versions | GET /schemas/{Id}/versions/diff |
norbix db schema index-status <id> |
the last schema-index run: the indexes Norbix made (idx_<field> per reference, uniq_<field> per unique field, idx_<field>__id for the default sort, at most 8), state building / ready / refused / partial per database |
GET /schemas/{Id}/index-status |
norbix db schema delete <id> [--yes] |
delete the schema and its records: the collection (records and indexes) in the environment is dropped, and removed from the AI knowledge when AI embed is on; no undo. Refused while a schema trigger uses it (CM-ERRORS-SCHEMA-017) or a saved aggregate starts on / joins it (CM-ERRORS-SCHEMA-018, the aggregates are in BlockerAggregateNames); then nothing is dropped |
DELETE /schemas/{Id} |
Design rules first. Run norbix db schema rules before writing
--file: an email is {"type":"string","format":"email"}, a link
{"type":"string","format":"uri"}; language / locale, currency code,
country and every list people pick from are a taxonomy picker
({"source":"taxonomy","taxonomyId":"<id>"}, an enum only for a
developer-facing flag); a person is a user reference
({"source":"user","displayField":"displayName"}). When a field breaks one of
these rules the Hub still saves the schema and answers with warnings, for
example:
Warning: the schema was saved, but 2 fields break a design rule:
- app_link: 'app_link' looks like a link: use a string with "format": "uri" (UI widget website), not bare text.
- language: 'language' looks like a language: use a taxonomy-selection field (a language taxonomy, one term per language) so values are validated and translatable. Free text only when the value is deliberately not tied to any list.
Fix them and save again. Rules: norbix db schema rules
--file is the data schema (the JSON Schema of one record); --ui-file is the
UI schema the dashboard form uses. A change is a draft first: records keep the
published version until schema publish. publish asks first and sends the
server's confirmed: true, so it also publishes a change the server calls
breaking — read schema draft (or schema diff after a publish) before you
answer yes.
norbix db schema create --name orders --file orders.schema.json --ui-file orders.ui.json
norbix db schema update 66b2f0a1c3d4e5f6a7b8c9d0 --file orders.schema.json
norbix db schema draft 66b2f0a1c3d4e5f6a7b8c9d0
norbix db schema publish 66b2f0a1c3d4e5f6a7b8c9d0 --yes
norbix db schema diff 66b2f0a1c3d4e5f6a7b8c9d0 --from 1 --to 2A schema trigger runs an action (send an e-mail, a push, an SMS, call a webhook …) when records of a schema are inserted, updated or deleted.
| command | what it does | endpoint |
|---|---|---|
norbix db triggers [--schema <id>] [--page-size <n>] [--after <cursor>] |
list triggers | GET /schemas/triggers |
norbix db trigger <id> [--schema <id>] |
show one trigger | GET /schemas/triggers/{id} |
norbix db trigger create --file <trigger.json> [--schema <id>] [--id <triggerId>] [--order <n>] [--[no-]break-on-error] |
create (or, with --id, update) a trigger |
POST /schemas/triggers |
norbix db trigger enable <id> |
turn it on | PATCH /schemas/triggers/{triggerId}/enable |
norbix db trigger disable <id> [--yes] |
turn it off | PATCH /schemas/triggers/{triggerId}/disable |
norbix db trigger delete <id> [--yes] |
delete it | DELETE /schemas/triggers/{triggerId} |
Schema triggers belong to one environment. Every trigger command works on the
copy in the request environment (--env, PROD when none is set): db triggers
lists only that environment's triggers (each row has env), and enable /
disable / delete of an id with no copy there answers CM-ERRORS-TRIGGERS-002
(not found). db trigger <id> shows schemaId (the owning schema, sch_…)
and env. trigger create --id with an id that belongs to another schema is
also CM-ERRORS-TRIGGERS-002.
The file holds the trigger — the same shape db trigger <id> --json shows:
name, schemaId, the record events it reacts to, and action ({type, …}).
"type": "Schema" is added when the file leaves it out. A file that holds the
whole request ({"trigger": {…}}) works too. --schema and --id win over the
file. The action names an integration and a template of the e-mail, push or SMS
module: look them up with norbix email integrations / norbix email templates
(and the push / sms equivalents).
When several triggers fire for the same record event they run as a queue.
--order <n> sets this trigger's place (0 or more, lower runs first; a trigger
without an order runs after the numbered ones, equal places run by name) and
--break-on-error stops the later triggers of the event when this one's action
fails (--no-break-on-error turns it off). Both win over order /
breakOnError in the file; db trigger <id> shows them back. An update
replaces the whole trigger, so keep the rest of the file as it was.
norbix db trigger create --file notify-on-order.json --schema 66b2f0a1c3d4e5f6a7b8c9d0
norbix db trigger create --file notify-on-order.json --id 66b2f0a1c3d4e5f6a7b8c9d1 --order 1 --break-on-error
norbix db trigger disable 66b2f0a1c3d4e5f6a7b8c9d1 --yes| command | what it does | endpoint |
|---|---|---|
norbix db integrations [--page-size <n>] [--after <cursor>] |
list the database integrations | GET /integrations |
norbix db integration <id> |
show one | GET /integrations/{id} |
norbix db integration test <id> |
check that the database behind it answers (writes nothing) | POST /integrations/test |
norbix db integration default <id> |
make it the project default | PUT /integrations/{Id}/default |
norbix integrations database lists them too, for scripts that treat every
module alike.
| command | what it does | endpoint |
|---|---|---|
norbix db taxonomies |
list taxonomies | GET /taxonomies |
norbix db taxonomy <id> |
show one taxonomy | GET /taxonomies/{id} |
norbix db terms <taxonomyName> [--filter <json>] [--page-size <n>] [--after <cursor>] [--desc] |
list the terms of a taxonomy | GET /taxonomies/{taxonomyName}/terms |
norbix db terms <taxonomyName> --parent <termId> |
only the children of one term | GET /taxonomies/{taxonomyName}/terms/{parentId}/children |
norbix db term <taxonomyId> <id> |
show one term | GET /taxonomies/{TaxonomyId}/terms/{Id} |
norbix db term tree <taxonomyName> [--root <termId>] [--depth <n>] |
the terms as a tree | GET /taxonomies/{TaxonomyName}/terms/tree |
norbix db term tree <taxonomyName> --merged |
one tree across the parent taxonomies | GET /taxonomies/{TaxonomyName}/merged-tree |
norbix db term create <taxonomyId> --doc <json> |
add a term | POST /taxonomies/{TaxonomyId}/terms |
norbix db term update <taxonomyId> <id> --set <json> |
change some fields of a term | PUT /taxonomies/{TaxonomyId}/terms/{Id} |
norbix db term delete <taxonomyId> <id> [--yes] |
delete a term | DELETE /taxonomies/{TaxonomyId}/terms/{Id} |
Note the two kinds of key: listing and trees take the taxonomy name; a single
term is addressed by the taxonomy id and the term id (that is how the
gateway routes them). --set is applied with $set: only the fields you give
change — name (a string or a {lang: value} map), description, order
(lower shows first), parentId, multiParents.
db taxonomies lists each taxonomy's parents as dependencyRefs
([{id, name}], in the order of dependencies; a parent that no longer exists
keeps its place with name: null).
Reading terms by taxonomy name (terms, term tree, term tree --merged)
needs read rights on that taxonomy's terms (database:term:<taxonomy id>); the
merged tree checks every parent taxonomy too. Errors you may meet:
CM-ERRORS-TAXONOMIES-010 (no taxonomy with that name),
CM-ERRORS-TAXONOMIES-011 (the tree has more than 5000 terms — read a
--root branch or page with terms instead) and CM-ERRORS-TAXONOMIES-005
(a name longer than 40 characters).
norbix db term create 66b2f0a1c3d4e5f6a7b8c9d0 --doc '{"name":"Lithuania","order":1}'
norbix db term update 66b2f0a1c3d4e5f6a7b8c9d0 66b2f0a1c3d4e5f6a7b8c9d1 --set '{"order":2}'
norbix db term tree countries --depth 2--jsonprints the server's answer (or the dry-run report) as JSON on stdout.--dry-runon every write prints the SDK call and the HTTP request, and sends nothing — the server does not check the values.db schema create --dry-rundoes not validate the JSON Schema file. A schema bundle can be checked:norbix hub database schema bundle apply --bundleJson '<IF json>' --dry-runasks the Hub's read-onlyaccount.validateSchemaand exits 6 with the issues when the bundle is invalid.delete,discard,publish,seed,trigger disable,trigger delete,term deleteand the--many/--allrecord writes ask first;--yesskips the question, and without a terminal they exit 3 unless--yesis given.- Exit codes are in the README ("Errors and exit codes").
These Database routes exist in the gateway and the SDKs, but are not commands
yet: they are dashboard settings, or their body is the dashboard's own editor
state. norbix hub database <words…> reaches every one of them.
| endpoint | why there is no command |
|---|---|
POST /taxonomies, DELETE /taxonomies/{Id}, GET /taxonomies/tree, DELETE /taxonomies/{TaxonomyId}/terms/many |
Taxonomy structure (dependencies, translatable names) is edited in the dashboard; terms — the data — have commands. |
POST /schemas/apply-bundle |
Applies a whole generated bundle of schemas; it is the AI schema builder's step, not a hand-run one. |
PUT /schemas/{Id}/rename, /embed, /list-settings, /settings |
Dashboard settings of one schema (title, embedding, list columns, soft delete / owner). |
POST /integrations, DELETE /integrations/{Id}, PUT /integrations/{Id}/enable / disable, GET /integrations/flex-tiers, GET /integrations/{Id}/connection-string |
Creating or removing a database, and revealing its connection string, are account-level decisions taken in the dashboard; a connection string should not land in a shell history. |
POST /aggregates, DELETE /aggregates/{Id}, GET /aggregates/{Id}, POST /aggregates/test |
Saved aggregates are written in the dashboard's pipeline editor; db aggregate --id runs one and db aggregate --pipeline runs any pipeline. |
/imports/** |
The CSV / JSON import flow (upload URL, analyze, create) is a dashboard wizard; out of the audit scope. |
Hub /collections/** record routes |
The same record operations as the data-plane API routes the record commands already use. |