Custom objects

Ship whole data models: objects, fields, formulas, validation and field-level security — provisioned per tenant on install, with an auto-generated CRUD API.

Field types

typenotes
text / textareastrings (single / long)
number / currency / percentnumerics; currency/percent affect display
date / datetimeISO YYYY-MM-DD / RFC3339
email / phone / urlformat-validated on write (https for urls)
picklist / multi_selectrequire options[]; values validated against them
checkboxboolean
referencelookup; referenceTarget = lead | contact | company | opportunity | custom:<apiName>. This is how your objects RELATE to CRM records.
formulacomputed by the platform on read — never writable

Formula language

Spreadsheet-style expressions over the record's own fields:

win_rate  = closed_won / (closed_won + closed_lost) * 100
label     = IF(score >= 80, "hot", "cold")
age_days  = DAYS_BETWEEN(created_date, TODAY())
categoryavailable
Operators+ - * / % · == != > >= < <= · && || ! (or AND OR NOT)
FunctionsIF, CONCAT, ROUND, ABS, MIN, MAX, LEN, UPPER, LOWER, CONTAINS, TODAY, DAYS_BETWEEN, NUMBER, TEXT, BLANKVALUE
Null semanticsmissing fields and division-by-zero evaluate to null, never errors — half-filled records render fine

Validation rules

"validationRules": [
  { "type": "min", "value": 0 },
  { "type": "regex", "value": "^\\d{5}$", "message": "zip must be 5 digits" }
]

Types: min, max, minLength, maxLength, regex — each with an optional custom message. Add required and is_unique at the field level.

Field-level security

readRoles / writeRoles (role lists; empty = everyone). Reads strip protected fields from responses; writes are rejected with 403 FIELD_ACCESS_DENIED.

The auto-generated API

After install, the tenant's records live at:

GET    /api/crm/custom/{apiName}/records?q=&filterField=&filterValue=&page=&size=
POST   /api/crm/custom/{apiName}/records
GET    /api/crm/custom/{apiName}/records/{id}      # formulas computed, ACLs applied
PATCH  /api/crm/custom/{apiName}/records/{id}
DELETE /api/crm/custom/{apiName}/records/{id}
Sandbox installs prefix your object api-names with sbx_ so a later prod install never collides — write your connector against the apiName the install actually returns.

Plan limits

Custom-object count is plan-gated per tenant (Business 3 / Enterprise 10). An install that would exceed the tenant's limit fails atomically and rolls back — nothing half-provisioned is left behind.