Skip to main content

Collections

Collections store your data as items with defined properties. Each collection becomes a RESTful endpoint.

Naming

  • Unique within the API
  • Case-sensitive
  • Allowed characters: letters, digits, hyphens, underscores
  • The name becomes the endpoint path
/api/products
└── collection name

For external clients, replace /api with https://<region>.restapi.com/<api-name>. See Virtual Paths.

note

Renaming a collection changes its endpoint path.

Properties

Each collection can have up to 25 properties.

Property Rules

  • Unique names within the collection
  • Case-sensitive
  • Allowed characters: letters, digits, underscores
  • Must begin with a letter

Property Configuration

SettingDescription
NameUnique identifier for the property
TypeData type (string, integer, decimal, etc.)
RequiredIf true, must be provided on create
DefaultValue used when property is omitted
ValidationMin/max length or value constraints

Data Types

TypeDescription
stringText, max 1024 characters
integerWhole numbers (-2,147,483,648 to 2,147,483,647)
decimalDecimal numbers
booleantrue or false
dateDate only (YYYY-MM-DD)
date-timeDate and time in ISO 8601 format
guidUnique identifier (UUID format)
blobBinary data (files)
objectReference to another model

System Fields

Every collection you create comes with system fields that the platform manages — you don't declare them, and you can't set their values.

FieldTypeWhen it's set
idguidOn create — primary key, server-generated
createddate-timeOn create — never changes
createdByuser referenceOn create — the user who created the record¹
modifieddate-timeOn create, then on every update
modifiedByuser referenceOn create, then on every update¹
sequenceintegerOn create — insertion order, a stable sort key

¹ createdBy/modifiedBy are filled only when the write is authenticated with a user token — writes made with an API key or service token leave them empty. On read they expand to a nested { id, name, email } object, so you can select into them:

?select=name,total,createdBy.name,createdBy.email,modified

Because created and modified already exist, there's no need to add your own createdAt/updatedAt property to track when a record was created or changed. You can filter and select system fields, and sort by created, modified, sequence, or id.

note

user reference in the table above is just how a lookup at the built-in user collection is displayed — you can declare one yourself with typeName: "user".

To scope rows by who created them, you don't need to declare anything:

  • Own rows only — grant _CREATOR on the method. GET, PATCH and DELETE against the collection, or a view over it, are then filtered to rows whose createdBy is the caller — no extra property to declare. Queries are not filtered this way, and _CREATOR never grants creation; see Owner-Only Access for the full limits.
  • Creator as part of a security-policy pathcreatedBy is a real lookup at user, so it can carry a direction (TargetFilteredByEntity, ↑) like any other lookup. It is unset by default, and the Developer Portal doesn't offer it on system properties, so set it through the schema API — POST Schema matches properties by name, while PUT Schema matches by id, so send createdBy with its id or the direction is dropped without an error. Note that giving any lookup a direction makes the whole collection row-filtered, which also means anonymous requests to it are rejected.

Declare your own user lookup when ownership has to be someone other than the creator — an assignee, a transferable owner, an approver. Adding a second person property that only ever holds the creator duplicates createdBy.

Lookup Properties

Create relationships between collections:

{
"name": "customer",
"type": "lookup",
"target": "customers"
}

Access related data in queries or via the select parameter:

?select=orderDate,total,customer.name,customer.email

Self-Referencing Lookups

Collections can reference themselves, which is useful for hierarchical data structures:

{
"name": "categories",
"properties": [
{ "name": "name", "type": "string" },
{ "name": "parent", "type": "lookup", "target": "categories" }
]
}

Use cases:

  • Category trees with parent/child relationships
  • Organizational hierarchies
  • Threaded comments
  • File/folder structures

Validation

Required Fields

Required properties must be included in POST/PUT requests:

{ "name": "email", "type": "string", "required": true }

Missing required fields return 400 Bad Request.

String Constraints

Limit string length:

{
"name": "title",
"type": "string",
"minLength": 1,
"maxLength": 100
}

Numeric Constraints

Set value boundaries:

{
"name": "quantity",
"type": "integer",
"min": 0,
"max": 1000
}

Default Values

Provide defaults for optional properties:

{
"name": "status",
"type": "string",
"default": "pending"
}

Defaults apply to POST and PUT operations when the property is omitted.

Expression-Based Defaults

Use expressions for dynamic default values:

ExpressionDescription
now()Current UTC date-time
now() + 7D7 days from now
now() - 1H1 hour ago
newId()Generate unique GUID
10 + 5Numeric calculation

Date/time tokens: now(), D (days), H (hours), M (minutes), S (seconds). Tokens are case-insensitive.

{
"name": "publishedAt",
"type": "date-time",
"default": "now()"
}
{
"name": "dueDate",
"type": "date-time",
"default": "now() + 7D"
}

See Data Types for complete documentation on default value expressions.

Example Collection

{
"name": "products",
"properties": [
{ "name": "sku", "type": "string", "required": true },
{ "name": "name", "type": "string", "required": true, "maxLength": 200 },
{ "name": "description", "type": "string" },
{ "name": "price", "type": "decimal", "required": true, "min": 0 },
{ "name": "stock", "type": "integer", "default": 0, "min": 0 },
{ "name": "isActive", "type": "boolean", "default": true },
{ "name": "publishedAt", "type": "date-time", "default": "now()" },
{ "name": "category", "type": "lookup", "target": "categories" },
{ "name": "image", "type": "blob" }
]
}