Skip to content

Meta API

The boot endpoint provides all metadata the frontend shell needs to initialize. This includes the user session, permissions, navigation, page definitions, model schemas, and widget definitions.

See How It Works concept for the lifecycle context.

Boot Endpoint

GET /api/meta/boot

Authentication: Required. Returns 401 if no valid session.

Caching: Response is user-specific (contains permissions). Not cacheable by CDN. Client should cache locally and invalidate on role/permission changes.

Response Shape

typescript
interface BootResponse {
  user: BootUser;
  permissions: BootPermissions;
  navigation: NavigationTree[];
  pages: PageDefinition[];
  models: Record<string, ModelMeta>;
  widgets?: WidgetDefinitionMeta[];
}

BootUser

typescript
interface BootUser {
  id: string;
  name: string;
  email: string;
  roles: string[];
}
FieldTypeDescription
idstringUser record ID.
namestringDisplay name.
emailstringEmail address.
rolesstring[]All roles assigned to this user (including inherited).

BootPermissions

typescript
interface BootPermissions {
  models: Record<string, ModelPermissions>;
  pages: string[];
}
FieldTypeDescription
modelsRecord<string, ModelPermissions>Keyed by qualified model name. Merged result of all user roles.
pagesstring[]Page keys the user is allowed to access.

ModelPermissions

typescript
interface ModelPermissions {
  read?: boolean | 'own';
  write?: boolean | 'own';
  create?: boolean;
  delete?: boolean | 'own';
  fieldPermissions?: Record<string, { read?: boolean; write?: boolean }>;
}
FieldTypeDefaultDescription
readboolean | 'own'falseCan list and view records. When 'own', user can only read records they own.
writeboolean | 'own'falseCan update existing records. When 'own', user can only update records they own.
createbooleanfalseCan create new records.
deleteboolean | 'own'falseCan delete records. When 'own', user can only delete records they own.
fieldPermissionsRecord<string, {read?, write?}>undefinedPer-field overrides. If a field is not listed, it inherits from the model-level read/write.
typescript
interface NavigationTree {
  module: string;
  label: string;
  description?: string;
  icon?: string;
  color?: string;
  order?: number;
  type?: 'internal' | 'external';
  sections: NavigationTreeSection[];
}
FieldTypeDescription
modulestringModule name.
labelstringModule display label.
descriptionstringShort description of the module.
iconstringModule icon (Lucide name).
colorstringModule theme color.
ordernumberSort order for sidebar.
type'internal' | 'external'Whether the module links internally or to an external URL.
sectionsNavigationTreeSection[]Grouped navigation items (already filtered by user permissions).
typescript
interface NavigationTreeSection {
  section: string;
  items: NavigationTreeItem[];
}
typescript
interface NavigationTreeItem {
  page: string;
  label: string;
  icon?: string;
}

ModelMeta

typescript
interface ModelMeta {
  qualifiedName: string;
  label?: string;
  fields: FieldMeta[];
}
FieldTypeDescription
qualifiedNamestringmodule.model format.
labelstringHuman-readable model name.
fieldsFieldMeta[]All fields with their metadata.

FieldMeta

typescript
interface FieldMeta {
  name: string;
  type: string;
  label?: string;
  required?: boolean;
  searchable?: boolean;
  options?: readonly string[];
  relationship?: {
    type: 'link' | 'hasMany' | 'children' | 'manyToMany' | 'dynamicLink';
    model?: string;
    foreignKey?: string;
    through?: string;
    modelField?: string;
  };
}
FieldTypeDescription
namestringField identifier.
typestringField type (string, int, link, etc.).
labelstringDisplay label.
requiredbooleanWhether the field is required.
searchablebooleanWhether the field is included in search queries.
optionsreadonly string[]Enum values (only present for type: 'enum').
relationshipobjectRelationship metadata (only present for relationship fields).

Relationship metadata fields

FieldPresent WhenDescription
typeAlwaysRelationship type.
modellink, hasMany, children, manyToManyTarget model qualified name.
foreignKeyhasMany, childrenField on the related model that points back.
throughmanyToManyJunction table model name.
modelFielddynamicLinkField that stores the target model name at runtime.

Example Response

json
{
  "user": {
    "id": "usr_001",
    "name": "Jane Smith",
    "email": "jane@company.com",
    "roles": ["Sales Manager", "Sales User"]
  },
  "permissions": {
    "models": {
      "sales.invoice": {
        "read": true,
        "write": true,
        "create": true,
        "delete": false,
        "fieldPermissions": {
          "cost_center": { "read": true, "write": false }
        }
      }
    },
    "pages": ["invoices", "customers", "reports"]
  },
  "navigation": [
    {
      "module": "sales",
      "label": "Sales",
      "icon": "shopping-cart",
      "order": 10,
      "sections": [
        {
          "section": "Transactions",
          "items": [
            { "page": "invoices", "label": "Sales Invoices", "icon": "file-text" },
            { "page": "customers", "label": "Customers", "icon": "users" }
          ]
        }
      ]
    }
  ],
  "pages": [
    {
      "key": "invoices",
      "label": "Sales Invoices",
      "type": "collection",
      "body": [
        {
          "type": "split",
          "props": { "sizes": [60, 40] },
          "children": [
            {
              "type": "table",
              "source": { "model": "sales.invoice" },
              "children": [
                { "type": "column", "props": { "label": "Number" }, "bind": { "field": "number" } },
                {
                  "type": "column",
                  "props": { "label": "Customer" },
                  "bind": { "field": "customer_name" }
                },
                {
                  "type": "column",
                  "props": { "label": "Status" },
                  "children": [{ "type": "badge", "bind": { "field": "status" } }]
                }
              ]
            },
            {
              "type": "data",
              "source": { "model": "sales.invoice", "id": "$state.selectedId" },
              "children": [
                { "type": "input", "bind": { "field": "customer_id" } },
                { "type": "input", "bind": { "field": "status" } }
              ]
            }
          ]
        }
      ]
    }
  ],
  "models": {
    "sales.invoice": {
      "qualifiedName": "sales.invoice",
      "label": "Sales Invoice",
      "fields": [
        { "name": "id", "type": "string", "required": true },
        {
          "name": "customer",
          "type": "link",
          "label": "Customer",
          "required": true,
          "relationship": { "type": "link", "model": "sales.customer" }
        },
        { "name": "status", "type": "enum", "options": ["Draft", "Submitted", "Cancelled"] },
        {
          "name": "items",
          "type": "children",
          "relationship": {
            "type": "children",
            "model": "sales.invoice_item",
            "foreignKey": "invoice_id"
          }
        }
      ]
    }
  }
}

Error Response

json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Valid session required"
  }
}

Session Endpoints

Login

POST /api/core/session

Body:

json
{
  "email": "user@company.com",
  "password": "..."
}

Response:

json
{
  "data": {
    "token": "eyJhbG...",
    "expires_at": "2026-06-20T06:00:00.000Z"
  }
}

Logout

DELETE /api/core/session

Authentication: Required.

Response: 204 No Content