Schema operations

Collection schemas, SQL indexes and their state, namespaces, security rules.

Docs menu

collection

collection.create

Create an object collection: body name (1 to 64 characters, not starting with an underscore; 409 when it exists), optional acl_flags (integer bit set of the collection's access rules, 0 = none enforced) and identity (field names whose values make a record's key content-addressed). Answers 201 {name, created, acl_flags, identity?}. A schema is separate: schema.update.

Route
POST /v1/_meta/collections
Auth
App key tenant
MCP tool
substrio_collection_create
CLI
substrio op collection.create

Parameters

NameInTypeRequiredDescription
namebodystringYesThe collection's name, 1 to 64 characters, not starting with an underscore. Matches ^[^_].
acl_flagsbodyintegerNoA bit set of access rules (0, the default, enforces none); the owner-only bit gives each record's owner read, write and delete. Minimum 0.
identitybodyarray of stringNoField names whose values make a record's key content-addressed.

Response

FieldTypePresentDescription
namestringAlways
createdanyAlways
acl_flagsintegerAlways
identityarray of stringWhen set

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X POST "$SUBSTRIO_URL/v1/_meta/collections" \
  -H "Authorization: Bearer $SUBSTRIO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"name"}'

collection.delete

Delete one collection by name together with its records and its index, vector, text and sparse trees; 404 when it does not exist and 400 for a name starting with an underscore. There is no undo: take a backup first when unsure.

Route
DELETE /v1/_meta/collections/{name}
Auth
App key tenant
MCP tool
substrio_collection_delete
CLI
substrio op collection.delete

Parameters

NameInTypeRequiredDescription
namepathstringYes

Response

FieldTypePresentDescription
deletedanyAlways
namestringAlways
documentsintegerAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X DELETE "$SUBSTRIO_URL/v1/_meta/collections/$NAME" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

collection.list

List the app's collections, paged: the limit and cursor options in, next_cursor out. Call it first to learn what data exists; collection.stats counts one collection's records.

Route
GET /v1/_collections
GET /v1/_meta/collections
Auth
App key tenant
MCP tool
substrio_collection_list
CLI
substrio op collection.list

Parameters

NameInTypeRequiredDescription
limitqueryinteger (0 or more)Nothe most items per page (positive); absent returns every item.
cursorquerystringNoresume after the previous page: its next_cursor.

Response

A JSON object.

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/_collections" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

collection.stats

Read one collection's statistics: doc_count, indexes (each named: {kind: field, field, unique} for a declared field, {kind: vector|text|sparse} for a search index) with index_count, their number, and has_rag; 404 when it does not exist.

Route
GET /v1/{collection}/_stats
GET /v1/{collection}/stats
Auth
App key tenant
MCP tool
substrio_collection_stats
CLI
substrio op collection.stats

Parameters

NameInTypeRequiredDescription
collectionpathstringYes

Response

FieldTypePresentDescription
doc_countintegerAlways
index_countintegerAlways
has_ragbooleanAlways
vector_dimensionsintegerWhen set
auto_chunkbooleanWhen set
source_fieldstringWhen set
vector_fieldstringWhen set
search_analyticsobjectAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/$COLLECTION/_stats" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

index

index.create

Create an index. kind sql (the default): on a SQL table: body table, name, columns (each name, optional descending and prefix_length for a text column), optional unique and if_not_exists; answers {success, table, name}; an unknown table or column, or a name already used, is refused with the engine's message, errno and sqlstate. kind expiry: body collection, field, after_ms and optional existing and if_not_exists; documents whose field holds a date are hidden from every read once the date plus after_ms has passed, and the collection's sweeper deletes them then (their keys, counters and unique values go with them; a change event and an oplog delete follow); a document without the field never expires; a stored value that is not a date refuses the declaration (existing allow declares anyway); a secret field is refused (400 SECRET_FIELD: a secret is in no key); the field is declared indexed; answers {success, kind, collection, field, after_ms, armed_ms, state ready}; a collection holds one expiry (409 unless if_not_exists); index.list kind expiry reads it and index.drop kind expiry takes it away. kind collection: declare a field of a collection: body collection, field, optional unique (true: a field or dotted path no two documents share; false, the default: indexed, its keys built and kept) and if_not_exists; the field's keys are built before the answer {success, kind, collection, field, unique, state ready} (the answer is the readiness signal); the declaration lasts across restarts, and a unique one makes every write path refuse a duplicate with UNIQUE (409); fields (an array, instead of field) declares a compound, unique as a tuple (UNIQUE then names each member in fields and values), and nulls count makes a null or missing member a value of its own (default skip: a document with no value for a member is not checked); within document makes the field unique inside each document (an array at the path may not hold a value twice; documents may share it) and within both does both (default collection; one field with nulls skip, else 400); the same fields with the other nulls are 409, a document holding several values (an array) of a member is 409 naming the member, nulls count on a nested path is 400; two documents already sharing a value refuse a unique declaration with UNIQUE and store nothing; a field already declared is 409 unless if_not_exists; index.drop kind collection takes it away. kind space declares a unique space: body space and members ([{collection, field}], at least one); the fields share one namespace, so no value may appear in two places of the space (two documents of one member, or one document of each of two members; a document holding a value in two members of its own collection is allowed), refused with UNIQUE (409) on insert, update, patch and batch, and the writers of one space are ordered so concurrent writes of one value let exactly one through; each member becomes a unique field of its collection (made when it does not exist) and cannot be dropped alone; existing documents holding a value in two places refuse the declaration with INDEX_DUPLICATES naming the collection; nulls, within and compound members are 400; a space declared with other members is 409 unless it is dropped first; answers {success, kind, space, members, state ready}; index.drop kind space takes it away. With keyword true (and optional keyword_term_bytes) the field is declared keyword instead (see keyword); answers {success, kind, collection, field, keyword, keyword_term_bytes, state ready}; index.drop with keyword true takes it away. kind aggregate declares a live count and sum per group (or over the whole collection with no group): body collection, name, group (0 or more field names or {field, step} bucket parts), metrics ({name: "count" or {sum: field}, ...}, at least one) and optional where (a declared segment's name: only its members are counted); every write keeps the group's count and sums current, and groupBy/sum/avg/count chains over the same shape answer from them with no record read; a read while it is Building answers by the exact scan instead; declaring the same name with the same shape again answers success, with another shape 400; answers {success, kind, collection, name, group, metrics, where, state}; index.drop kind aggregate takes it away (404 KEY_NOT_FOUND unless the name is declared); index.list kind aggregate lists every one with its state. kind vector makes a dense vector field's index: body collection, field, dims (17 to 4096) and optional metric (l2, the default, or cosine; dot is 400 naming TV9-DOT, the TurboVector9 step that brings it), bands, route and clusters (TurboVector9's code; default by the dims); dims, metric and code are fixed when it is made, so declaring the same shape again answers success and another (or a field whose first vector already made it with another) is 409 with the held shape; answers {success, kind, collection, field, dims, metric, bands, route, clusters, created, vectors, trained, settled} once a retrain the vectors written so far made due is done (10 minutes at most; settled false past that), so declaring again after a load is the readiness signal; a $knn query takes probes, target and budget (the search's clusters probed at most, neighbour mass covered and candidates reranked; default by k). index.list shows its state.

Route
POST /v1/_indexes
Auth
App key tenant
MCP tool
substrio_index_create
CLI
substrio op index.create

Parameters

NameInTypeRequiredDescription
kindbody"sql" | "collection" | "space" | "expiry" | "aggregate" | "id_derive" | "vector"Nosql (the default): an index on a SQL table, which takes table, name and columns; vector: a dense vector field's index, which takes collection, field, dims and optional metric, bands, route and clusters; collection: a field of a collection, which takes collection, field (or fields) and unique; space: a unique space across collections, which takes space and members; expiry: the collection's documents are gone after_ms milliseconds after the date their field holds, which takes collection, field, after_ms, existing and if_not_exists; aggregate: a live count and sum per group (or over the whole collection), which takes collection, name, group, metrics and optional where.
groupbodyarray of anyNokind aggregate: 0 or more group parts, each a field name (plain top-level or dotted) or {field, step} (step buckets a numeric or date value, e.g. 86400000 for a day); an empty or absent group is the whole collection as one group.
metricsbodyobjectNokind aggregate (required): one or more named metrics, each the string "count" (the group's rows) or {"sum": field} (the field's sum).
wherebodystringNokind aggregate: a declared segment's name; only its members are counted (default: every document).
dimsbodyintegerNokind vector (required): the vectors' dims, fixed when the index is made; a vector of other dims is refused. Minimum 17. Maximum 4096.
metricbody"l2" | "cosine"Nokind vector: l2 (the default; distance is squared L2) or cosine (vectors and queries normalized once; distance is 1 - cosine); fixed when the index is made.
bandsbodyarray of integerNokind vector: the dims coded at 3, 2 and 1 levels, multiples of 4 (default: chosen by the dims).
routebodyintegerNokind vector: the dims clusters are routed on (default 128, at most dims). Minimum 1.
clustersbodyintegerNokind vector: a fixed cluster count (default: grows with the vectors). Minimum 1.
after_msbodyintegerNokind expiry: how long after the field's date a document is gone, in milliseconds (0: the date is the time it expires). A document with no value in the field never expires. Minimum 0.
existingbody"refuse" | "allow"Nokind expiry: refuse (the default) the declaration while a stored document holds a value in the field that is not a date (the error names the first), or allow it (such a document never expires).
tablebodystringNokind sql (required): the SQL table to index.
namebodystringNokind sql (required): the index's name.
columnsbodyarray of objectNokind sql (required): the indexed columns.
collectionbodystringNokind collection (required): the collection.
fieldbodystringNokind collection (field or fields): one field name, or a dotted path to a nested field (a.b.c).
fieldsbodyarray of stringNokind collection with unique (field or fields): the members of a compound unique declaration, each a field name or dotted path; the tuple of their values is unique (not with field).
nullsbody"skip" | "count"Nokind collection with unique: how a null or missing member counts: skip (the default; a document with no value for a member is not checked) or count (null and missing are one value, so two documents with the same other members and a null member conflict; top-level fields only). The same fields cannot be declared with both.
withinbody"collection" | "document" | "both"Nokind collection with unique: where the value must be unique: collection (the default; no two documents hold it), document (no document holds it twice, e.g. in an array at the path; documents may share it) or both. document and both are for one field with nulls skip. The same field cannot be declared with two values.
uniquebodybooleanNokind sql: a unique index; kind collection: true, no two documents hold the same value of the field; false (the default), the field is declared indexed: its keys are built and kept for as long as it is declared.
keywordbodybooleanNokind collection: declare the string field (or dotted path) keyword (cannot be combined with unique): each distinct word is keyed by its suffixes, each cut to keyword_term_bytes, so contains and icontains of a word read one key range and a one-word icontains reads no record.
keyword_term_bytesbodyintegerNokind collection with keyword: the longest term kept per suffix (default 64, at most the key size limit); a longer query word is answered from its first keyword_term_bytes bytes and finished on its candidate records; a field is declared with one length, drop it to change it. Minimum 1.
elementsbodyarray of stringNokind collection: declare the array field (field) as element tuples (cannot be combined with unique or keyword): each element object is keyed by the values of these members, plain names in the order the tuple holds them, so an elemMatch of equalities on the first members and one range on the next reads one key range and tests only those documents.
if_not_existsbodybooleanNo
spacebodystringNokind space (required): the unique space's name.
membersbodyarray of objectNokind space (required): the fields that share one namespace, each a field (or dotted path) of a collection; a collection that does not exist is made. No value may appear in two places of the space: two documents of one member, or one document of each of two members (a document holding a value in two members of its own collection is allowed).

Response

FieldTypePresentDescription
successbooleanAlways
tablestringWhen setkind sql: the table.
namestringWhen setkind sql: the index.
kind"collection" | "space"When setpresent for a collection field or a unique space.
collectionstringWhen set
fieldstringWhen set
uniquebooleanWhen set
state"ready"When setkind collection (create): the field's keys are built.
spacestringWhen setkind space: the unique space.
membersarray of objectWhen setkind space: the members of the space.

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X POST "$SUBSTRIO_URL/v1/_indexes" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

index.drop

Drop one index of a SQL table: the route's {name} and the required table option. Answers {success, table, name}; index.list shows the indexes. kind=collection with the collection option drops a collection's declaration: {name} is the field (a compound: the fields option names its members), every declaration of that member set (unique and indexed) goes, its keys stay and writes are no longer checked; answers {success, kind, collection, field}, 404 KEY_NOT_FOUND when the field was not declared; with keyword=true it drops the field's keyword declaration instead: its terms, id and declaration go (answers {success, kind, collection, field, keyword}); with elements (a JSON array of the tuple's members) it drops the array field's element declaration instead: its keys, id and declaration go (answers {success, kind, collection, field, elements}, 404 KEY_NOT_FOUND when it was not declared). kind=space drops the unique space named by {name}: its members no longer share a namespace and stay plain unique fields of their collections; answers {success, kind, space, members}, 404 KEY_NOT_FOUND when there is no such space. kind=aggregate with the collection option drops the aggregate named by {name}: its declaration and group records go; answers {success, kind, collection, name}, 404 KEY_NOT_FOUND when it was not declared.

Route
DELETE /v1/_indexes/{name}
Auth
App key tenant
MCP tool
substrio_index_drop
CLI
substrio op index.drop

Parameters

NameInTypeRequiredDescription
namepathstringYesThe index's name, as index.list names it; pass its table as the table option (kind=collection: the field, with the collection option; a compound is dropped by its fields option, {name} is then any label).
tablequerystringNothe SQL table (sql.schema and index.list: only this table; index.drop: required).
kindquery"sql" | "collection" | "conflicts" | "space" | "expiry" | "aggregate" | "id_derive"Nosql (the default): the SQL tables' indexes (index.list) or one SQL index to drop (index.drop); collection: index.list reports one field of a collection (whether it is declared unique or indexed, and the state of its value keys: give collection and field), or every field the collection declares when field is left out, index.drop takes the declaration of the route's {name} field away (give collection); conflicts (index.list): the documents that stop a collection field from being declared unique, those holding a value another document holds, in id order (give collection and field; page with limit and cursor); space: index.list reports the unique spaces with their members, index.drop takes the space named by the route's {name} away (its member fields stay plain unique fields of their collections); expiry: the collection's expiry declaration (index.list reports its field, after_ms and the instant the sweeper is armed for; index.drop takes it away; give collection).
collectionquerystringNokind=collection or conflicts: the collection to report on or drop a declaration from (required with those kinds).
keywordquerybooleanNoindex.drop kind=collection: true drops the field's keyword declaration (its suffix terms and declaration) instead of its unique or indexed declaration.
fieldsquerystringNokind=conflicts: the members of a compound unique declaration, a JSON array of field names such as ["event","user"], instead of field; index.drop kind=collection: the member set of the declaration to drop, in place of the route's {name} (any label).
elementsquerystringNoindex.drop kind=collection: the members of the element declaration to drop, a JSON array of names such as ["sku","qty"], with the route's {name} the array field: its keys, id and declaration go.
compoundquerystringNoindex.drop kind=collection: the compound declaration to drop, a JSON array of top-level field names, the lead fields then the order field last, such as ["event","user","at"] (the route's {name} is any label): its keys, id and declaration go.

Response

FieldTypePresentDescription
successbooleanAlways
tablestringWhen setkind sql: the table.
namestringWhen setkind sql: the index.
kind"collection" | "space"When setpresent for a collection field or a unique space.
collectionstringWhen set
fieldstringWhen set
uniquebooleanWhen set
state"ready"When setkind collection (create): the field's keys are built.
spacestringWhen setkind space: the unique space.
membersarray of objectWhen setkind space: the members of the space.

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X DELETE "$SUBSTRIO_URL/v1/_indexes/$NAME" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

index.list

List the indexes of the SQL tables with each one's value-key state: state ready (every row has its key and every write keeps it), building (a first query is posting the keys now) or none (no key kept; reason says why; a failed build reports none and the engine retries it). Pass table for one table. A collection's field is keyed by its first query, or declared with index.create kind collection: pass kind collection with collection and field to read whether the field is declared (unique, or indexed: a unique field is indexed too) and its key state and the reason it has no key; without field it lists every field the collection declares. Pass kind conflicts with collection and field (or fields, a JSON array, for a compound, with nulls skip or count) to list what stops the field from being declared unique (a declaration over duplicates answers INDEX_DUPLICATES with the first and the count): {conflicts: [{_id, value}], count, next_cursor}, the documents that hold a value another document holds, in id order, paged with limit and cursor; it builds the field's keys when they are not kept yet. Pass kind space to list the unique spaces: {spaces: [{space, members: [{collection, field}]}], count}. Pass kind expiry with collection to read the collection's expiry declaration {field, after_ms, armed_ms}. A collection's keyword and element declarations are listed beside its fields, each its own entry: keyword true with keyword_term_bytes, or elements (the tuple's members), with state and reason. Pass kind aggregate with collection to list the collection's declared live aggregates (index.create kind aggregate): {name, group, metrics, where, state}.

Route
GET /v1/_indexes
Auth
App key tenant
MCP tool
substrio_index_list
CLI
substrio op index.list

Parameters

NameInTypeRequiredDescription
limitqueryinteger (0 or more)Nothe most items per page (positive); absent returns every item.
cursorquerystringNoresume after the previous page: its next_cursor.
tablequerystringNothe SQL table (sql.schema and index.list: only this table; index.drop: required).
kindquery"sql" | "collection" | "conflicts" | "space" | "expiry" | "aggregate" | "id_derive"Nosql (the default): the SQL tables' indexes (index.list) or one SQL index to drop (index.drop); collection: index.list reports one field of a collection (whether it is declared unique or indexed, and the state of its value keys: give collection and field), or every field the collection declares when field is left out, index.drop takes the declaration of the route's {name} field away (give collection); conflicts (index.list): the documents that stop a collection field from being declared unique, those holding a value another document holds, in id order (give collection and field; page with limit and cursor); space: index.list reports the unique spaces with their members, index.drop takes the space named by the route's {name} away (its member fields stay plain unique fields of their collections); expiry: the collection's expiry declaration (index.list reports its field, after_ms and the instant the sweeper is armed for; index.drop takes it away; give collection).
collectionquerystringNokind=collection or conflicts: the collection to report on or drop a declaration from (required with those kinds).
fieldquerystringNokind=collection: the field to report on (all the collection's declared fields when left out); kind=conflicts: the field (required, or fields for a compound).
fieldsquerystringNokind=conflicts: the members of a compound unique declaration, a JSON array of field names such as ["event","user"], instead of field; index.drop kind=collection: the member set of the declaration to drop, in place of the route's {name} (any label).
nullsquery"skip" | "count"Nokind=conflicts: how a null or missing member counts in the declaration listed, as index.create nulls: skip (the default; a document with no value for a member is not checked) or count (null and missing are one value).
withinquery"collection" | "document" | "both"Nokind=conflicts: where the declaration listed is unique, as index.create within: collection (the default; the documents sharing a value), document (the documents holding a value twice) or both; document and both are for one field with nulls skip, else 400.

Response

FieldTypePresentDescription
indexesarray of objectWhen set
spacesarray of objectWhen setkind space: the unique spaces, in name order.
conflictsarray of objectWhen setkind conflicts: the documents holding a value another document holds, in id order.
countintegerAlwaysthe items of this page.
next_cursorstring or nullWhen setkind conflicts: pass as cursor for the next page; null on the last.

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/_indexes" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

namespace

namespace.delete

Delete one namespace of a collection by name (the route's {collection} and {name}).

Route
DELETE /v1/{collection}/_namespaces/{name}
Auth
App key tenant
MCP tool
substrio_namespace_delete
CLI
substrio op namespace.delete

Parameters

NameInTypeRequiredDescription
collectionpathstringYesThe collection's name, as collection.list names it.
namepathstringYesThe namespace's name, as namespace.list names it.

Response

FieldTypePresentDescription
deletedanyAlways
collectionstringAlways
namespacestringAlways
documentsintegerAlways
trees_recycledintegerAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X DELETE "$SUBSTRIO_URL/v1/$COLLECTION/_namespaces/$NAME" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

namespace.list

List the namespaces of one collection (the route's {collection}); namespace.stats reads one.

Route
GET /v1/{collection}/_namespaces
Auth
App key tenant
MCP tool
substrio_namespace_list
CLI
substrio op namespace.list

Parameters

NameInTypeRequiredDescription
collectionpathstringYes

Response

FieldTypePresentDescription
collectionstringAlways
namespacesarray of stringAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/$COLLECTION/_namespaces" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

namespace.stats

Read the statistics of one namespace of a collection (the route's {collection} and {name}).

Route
GET /v1/{collection}/_namespaces/{name}/stats
Auth
App key tenant
MCP tool
substrio_namespace_stats
CLI
substrio op namespace.stats

Parameters

NameInTypeRequiredDescription
collectionpathstringYesThe collection's name, as collection.list names it.
namepathstringYesThe namespace's name, as namespace.list names it.

Response

FieldTypePresentDescription
collectionstringAlways
namespacestringAlways
documentsintegerAlways
treesobjectAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/$COLLECTION/_namespaces/$NAME/stats" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

schema

schema.delete

Delete a collection's schema (the route's {collection}).

Route
DELETE /v1/{collection}/_schema
Auth
App key tenant
MCP tool
substrio_schema_delete
CLI
substrio op schema.delete

Parameters

NameInTypeRequiredDescription
collectionpathstringYes

Response

FieldTypePresentDescription
okanyAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X DELETE "$SUBSTRIO_URL/v1/$COLLECTION/_schema" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

schema.get

Read a collection's schema: its rules (field, type, required, min, max, pattern, enum), its constraints across fields when it has any, and whether it is strict.

Route
GET /v1/{collection}/_schema
Auth
App key tenant
MCP tool
substrio_schema_get
CLI
substrio op schema.get

Parameters

NameInTypeRequiredDescription
collectionpathstringYes

Response

FieldTypePresentDescription
collectionstringAlways
strictbooleanAlways
rulesarray of objectAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/$COLLECTION/_schema" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

schema.update

Set a collection's schema, replacing the old one: body rules (each rule's field is a document field name kept exactly as given, an underscore first included; _id, _createdAt, _updatedAt and _version are the system's and refused), optional constraints and strict (default true, which refuses fields no rule names; every answer states the strict in force); at least one rule or constraint. 404 COLLECTION_NOT_FOUND when the collection does not exist: create it first. A rule's type is string, number, boolean, array, object, datetime (an RFC 3339 / ISO 8601 date-time string, kept as given, or an integer epoch in milliseconds), secret (a string field no read returns: it is left out of every document, result, export and event, a filter, sort, group or projection naming it answers 400 SECRET_FIELD, and it takes no enum, constraint, unique, index, text, keyword, top or embedding; a function reads it with db.secret) or any; any other word is refused with 400 INVALID_ARGUMENT. null counts as absent: a field that is not required accepts null, a required one refuses it unless its rule sets nullable to true; min and max bound a number's value or a string's length; pattern is a regular expression for strings; enum lists the allowed strings. A constraint is {kind, ...}: exactly_one, at_least_one or at_most_one with fields (two or more), or required_when with field, equals, required and forbidden (for example {"kind":"required_when","field":"kind","equals":"x","required":["a"],"forbidden":["c"]}); every field a constraint names needs a string, number, boolean or datetime rule in the same schema. A write that breaks a constraint answers 400 SCHEMA_REFUSED with the violation's constraint kind and fields (never a value). Stored documents that already break a constraint refuse the declaration with 409 SCHEMA_VIOLATORS ({count, first:{_id}}); existing "allow" declares anyway. Answers the stored schema.

Route
PATCH /v1/{collection}/_schema
POST /v1/{collection}/_schema
Auth
App key tenant
MCP tool
substrio_schema_update
CLI
substrio op schema.update

Parameters

NameInTypeRequiredDescription
collectionpathstringYesThe collection whose schema to set.
strictbodybooleanNotrue (the default when absent) refuses fields no rule names; every schema answer states the strict in force.
existingbody"refuse" | "allow"NoWhat to do when stored documents already break a constraint: refuse (the default) declares nothing and answers 409 SCHEMA_VIOLATORS with the count and the first _id; allow declares the constraint anyway (later writes are checked, the stored documents are left as they are). Default "refuse".
rulesbodyarray of objectNoThe field rules; the schema is replaced as a whole.
constraintsbodyarray of objectNoRules across fields, checked on the final document of every write (an insert, update, patch, upsert or put); a field is present when it holds a value other than null. An invalid one is refused with INVALID_ARGUMENT naming its index.

Response

FieldTypePresentDescription
collectionstringAlways
strictbooleanAlways
rulesarray of objectAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X PATCH "$SUBSTRIO_URL/v1/$COLLECTION/_schema" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

security

security.rules.delete

Delete the app's collection security rules; answers {ok}.

Route
DELETE /v1/_rules
Auth
App key tenant
MCP tool
substrio_security_rules_delete
CLI
substrio op security.rules.delete

Parameters

No parameters.

Response

A JSON object.

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X DELETE "$SUBSTRIO_URL/v1/_rules" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

security.rules.get

Read the app's collection security rules: which callers may read, create, write and delete in each collection.

Route
GET /v1/_rules
Auth
App key tenant
MCP tool
substrio_security_rules_get
CLI
substrio op security.rules.get

Parameters

No parameters.

Response

A JSON object.

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/_rules" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"

security.rules.set

Replace the app's collection security rules: body rules, a list of {collection (or * for every collection without a rule of its own), read, write, create, delete}, each access one of allow, auth, owner, deny or role:<name>. Answers the stored rules. A version can carry rules too (version.create).

Route
POST /v1/_rules
Auth
App key tenant
MCP tool
substrio_security_rules_set
CLI
substrio op security.rules.set

Parameters

NameInTypeRequiredDescription
rulesbodyarray of objectYes

Response

A JSON object.

Errors

A failed call answers the error envelope with a typed code.

Example

curl -X POST "$SUBSTRIO_URL/v1/_rules" \
  -H "Authorization: Bearer $SUBSTRIO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rules":[]}'

sql

sql.schema

Describe the SQL catalog from the engine's typed descriptors: tables and views with columns (name, declared type, nullable, default, key, generated), indexes (columns with order, unique), foreign keys, and routines; pass table for one table.

Route
GET /v1/_sql/schema
Auth
App key tenant
MCP tool
substrio_sql_schema
CLI
substrio op sql.schema

Parameters

NameInTypeRequiredDescription
tablequerystringNothe SQL table (sql.schema and index.list: only this table; index.drop: required).

Response

FieldTypePresentDescription
tablesarray of objectAlways
routinesarray of objectAlways

Errors

A failed call answers the error envelope with a typed code.

Example

curl "$SUBSTRIO_URL/v1/_sql/schema" \
  -H "Authorization: Bearer $SUBSTRIO_KEY"