Layouts API
REST API reference for page layouts, assignments, resolution, and entity listing
Overview
The Layouts API lets you manage page layouts, assign them to users/roles/groups, and resolve which layout applies for a given user. All endpoints require authentication.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/settings/layouts | List all layouts |
| POST | /api/settings/layouts | Create a layout |
| GET | /api/settings/layouts/{id} | Get a single layout |
| PATCH | /api/settings/layouts/{id} | Update a layout |
| DELETE | /api/settings/layouts/{id} | Delete a layout |
| GET | /api/settings/layouts/resolve | Resolve the best layout for the current user |
| POST | /api/settings/layouts/defaults | Seed default form layouts for standard entities |
| GET | /api/settings/layout-assignments | List all assignments |
| POST | /api/settings/layout-assignments | Create an assignment |
| DELETE | /api/settings/layout-assignments | Delete an assignment |
| GET | /api/settings/entities | List all registered entities |
| POST | /api/admin/seed-better-layouts | Regenerate multi-section default layouts |
List Layouts
GET /api/settings/layoutsQuery Parameters:
| Param | Type | Description |
|---|---|---|
entity | string | Filter by target entity (e.g., Student) |
type | string | Filter by layout type (e.g., LT_FORM) |
Response:
{
"data": [
{
"id": "clx...",
"name": "Student Form",
"targetType": "STATIC_ENTITY",
"targetEntity": "Student",
"layoutType": "LT_FORM",
"isDefault": true,
"isActive": true,
"config": { "sections": [...] },
"folderId": null,
"createdBy": { "name": "Admin", "email": "admin@school.com" },
"assignmentCount": 3,
"createdAt": "2026-04-13T...",
"updatedAt": "2026-04-13T..."
}
]
}Create Layout
POST /api/settings/layoutsRequest Body:
{
"name": "Student Admissions Form",
"targetType": "STATIC_ENTITY",
"targetEntity": "Student",
"layoutType": "LT_FORM",
"folderId": null
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name for the layout |
targetType | string | Yes | STATIC_ENTITY or DYNAMIC_OBJECT |
targetEntity | string | Yes | Entity API name (e.g., Student, Vehicle) |
layoutType | string | Yes | LT_DETAIL, LT_LIST, LT_FORM, LT_DASHBOARD, or LT_PORTAL |
folderId | string | No | Folder ID for organization |
Response: 201 Created with the created layout object.
The layout is created with an empty config ({ sections: [] }). Use the designer or PATCH to add sections.
Get Layout
GET /api/settings/layouts/{id}Returns the full layout object including the config JSON.
Update Layout
PATCH /api/settings/layouts/{id}Request Body (all fields optional):
{
"name": "Updated Name",
"config": {
"sections": [
{
"id": "personal",
"title": "Personal Information",
"columns": 2,
"collapsible": false,
"isCollapsed": false,
"widgets": [
{
"id": "personal_fields",
"type": "FIELD_GROUP",
"config": {
"fields": ["firstName", "lastName", "dob", "gender"]
}
}
]
}
]
},
"isDefault": true,
"folderId": "clx..."
}Config Structure:
The config object follows this schema:
interface LayoutConfig {
sections: Section[];
}
interface Section {
id: string; // Unique section ID
title: string; // Display title
columns: 1 | 2; // Column layout
collapsible: boolean; // Can be expanded/collapsed
isCollapsed: boolean; // Default collapsed state
widgets: Widget[]; // Widgets in this section
}
interface Widget {
id: string; // Unique widget ID
type: "FIELD_GROUP" | "ATTACHMENTS" | "NOTES";
config: {
fields?: string[]; // Field API names (for FIELD_GROUP)
};
}Delete Layout
DELETE /api/settings/layouts/{id}Returns { success: true }. Cascade deletes related layout assignments.
Resolve Layout
GET /api/settings/layouts/resolve?entity=Student&type=LT_FORMResolves the best-matching layout for the authenticated user.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
entity | string | Yes | Entity API name |
type | string | Yes | Layout type |
Response:
{
"layout": {
"id": "clx...",
"name": "Student Form",
"config": { "sections": [...] },
"layoutType": "LT_FORM",
"targetEntity": "Student"
},
"reason": "ROLE"
}Resolution Priority:
| Priority | Reason | Description |
|---|---|---|
| 1 | USER | Layout assigned directly to this user |
| 2 | GROUP | Layout assigned to a group the user belongs to |
| 3 | ROLE | Layout assigned to the user's role |
| 4 | PROFILE_TYPE | Layout assigned to the user's profile type |
| 5 | DEFAULT | Layout marked as default for this entity + type |
| 6 | NONE | No layout found — client renders fallback |
Results are cached in Redis for 5 minutes. NONE results are never cached.
Seed Default Form Layouts
POST /api/settings/layouts/defaultsCreates default form layouts for standard entities (Student, Staff, Enquiry, User) if they don't already exist. Skips entities that already have a form layout.
Response:
{
"success": true,
"created": ["Student", "User"],
"skipped": ["Staff", "Enquiry"],
"message": "Created 2 default form layout(s). Skipped 2 (already exist)."
}List Assignments
GET /api/settings/layout-assignmentsReturns all layout assignments for the current institution.
Response:
{
"data": [
{
"id": "clx...",
"layoutId": "clx...",
"entity": "Student",
"layoutType": "LT_FORM",
"assignmentType": "ASSIGN_ROLE",
"profileType": null,
"isActive": true,
"priorityOverride": null,
"layout": { "id": "clx...", "name": "Admissions Form", "layoutType": "LT_FORM", "targetEntity": "Student" },
"role": { "id": "clx...", "name": "Admissions Counsellor" },
"group": null,
"user": null,
"createdBy": { "name": "Admin" }
}
]
}Create Assignment
POST /api/settings/layout-assignmentsRequest Body:
{
"layoutId": "clx...",
"entity": "Student",
"layoutType": "LT_FORM",
"assignmentType": "ASSIGN_ROLE",
"roleId": "clx..."
}| Field | Type | Required | Description |
|---|---|---|---|
layoutId | string | Yes | Layout to assign |
entity | string | Yes | Entity API name |
layoutType | string | Yes | Layout type |
assignmentType | string | Yes | ASSIGN_PROFILE_TYPE, ASSIGN_ROLE, ASSIGN_GROUP, or ASSIGN_USER |
profileType | string | Conditional | Required if assignmentType is ASSIGN_PROFILE_TYPE. Values: PROFILE_STUDENT, PROFILE_PARENT, PROFILE_STAFF, PROFILE_ADMIN, PROFILE_SUPER_ADMIN |
roleId | string | Conditional | Required if assignmentType is ASSIGN_ROLE |
groupId | string | Conditional | Required if assignmentType is ASSIGN_GROUP |
userId | string | Conditional | Required if assignmentType is ASSIGN_USER |
priorityOverride | number | No | Override the default priority ordering |
Duplicate Check: Returns 409 Conflict if an assignment already exists for the same entity + layoutType + assignmentType + target.
Cache: Automatically invalidates layout resolution cache for the affected entity.
Delete Assignment
DELETE /api/settings/layout-assignments?id={assignmentId}Returns { success: true }. Invalidates layout resolution cache.
List All Entities
GET /api/settings/entitiesReturns all registered ObjectDefinition records (both standard and dynamic), ordered by section and display order. Used by the Layout Manager UI for entity dropdowns.
Response:
{
"data": [
{
"id": "clx...",
"name": "Student",
"apiName": "Student",
"pluralName": "Students",
"icon": "GraduationCap",
"color": "#3b82f6",
"section": "CORE",
"isStandard": true,
"displayOrder": 10
},
{
"id": "clx...",
"name": "Vehicle",
"apiName": "Vehicle",
"pluralName": "Vehicles",
"icon": "Bus",
"color": "#0284c7",
"section": "OPERATIONS",
"isStandard": false,
"displayOrder": 11
}
]
}Sections: CORE, ADMIN, OPERATIONS, FINANCE
Seed Better Default Layouts
POST /api/admin/seed-better-layoutsRequires: SUPER_ADMIN or INSTITUTION_ADMIN role.
Replaces single-section default layouts with properly grouped multi-section layouts for all ~40 entities. Safe to call multiple times — updates existing defaults without duplicating.
What it does:
- Updates existing default Form and Detail layouts with multi-section configs
- Creates missing default layouts if they don't exist
- Never touches non-default layouts (custom layouts are safe)
Response:
{
"success": true,
"updated": ["Student Form", "Student Detail", "Vehicle Form", "..."],
"created": ["Concession Form", "Concession Detail"],
"skipped": [],
"message": "Updated 78, created 4, skipped 0 layouts."
}Example layout structure after seeding:
Vehicle Form:
{
"sections": [
{ "id": "basic", "title": "Vehicle Information", "columns": 2, "fields": ["registrationNo", "type", "make", "model", "capacity"] },
{ "id": "personnel", "title": "Personnel", "columns": 2, "fields": ["driverId", "conductorId"] },
{ "id": "compliance", "title": "Compliance & Expiry", "columns": 2, "fields": ["insuranceExpiry", "fitnessExpiry", "permitExpiry", "pucExpiry"] },
{ "id": "tracking", "title": "Tracking", "columns": 2, "fields": ["gpsDeviceId", "isActive"] }
]
}Error Codes
| Status | Meaning |
|---|---|
| 401 | Not authenticated |
| 403 | Insufficient permissions (admin-only endpoints) |
| 404 | Layout or assignment not found |
| 409 | Duplicate assignment — one already exists for this target |