Code Examples

Practical examples for common P9 patterns, API integrations, and real-world configurations.

P9 Examples

Complete Object: Service Request

A facilities service request with cascading lookups, conditional fields, and table details:

@object ServiceRequest
@application FacilityManagement
@icon "clipboard-icon"
@llmspec "Tenant service request for maintenance. Links to Building, Unit, Category. Creates WorkOrders."

field Number type:Autonumber format:"SR-{yy}-#" label:"Number" search-filter:true filter-order:"1"
field Name type:Text label:"Title" required:true search-field:true
field Description type:Text label:"Description" multi-line:true maximum:"2000" optional:true
field Building type:Lookup label:"Building" required:true advance-search:true linked-view:true
field Unit type:Lookup label:"Unit" optional:true parent:Building advance-search:true
field Tenant type:Lookup label:"Tenant" optional:true advance-search:true linked-view:true
field Category type:Lookup label:"Category" optional:true advance-search:true
field SubCategory type:Lookup label:"Sub-Category" optional:true parent:Category
field Priority type:Select label:"Priority"
field Photos type:Attachment label:"Photos" optional:true
field Status type:Select label:"Status"
field Supervisor type:Lookup label:"Assigned Supervisor" optional:true advance-search:true

select Priority {
	label:"Low" value:"Low"
	label:"Medium" value:"Medium" default:true
	label:"High" value:"High"
	label:"Critical" value:"Critical"
}

select Status {
	label:"Open" value:"Open" default:true
	label:"Pending" value:"Pending"
	label:"InProgress" value:"InProgress"
	label:"Scheduled" value:"Scheduled"
	label:"Completed" value:"Completed"
	label:"Closed" value:"Closed"
	label:"Cancelled" value:"Cancelled"
}

lookup Building {
	lookupobjectname:"Building"

	displayfield {
		"Name"
		"Number"
		"Emirate"
	}

	filter {
		Status="Active"
	}
}

lookup Unit {
	displayfield {
		"Name"
		"Floor"
		"Building"
		"UnitProperty"
	}

	parents {
		Building {
			parentobjectfield: "Building"
		}
	}

	filter {
		Status in "Occupied,Available,Active"
	}
}

lookup Tenant {
	lookupobjectname:"Tenant"

	displayfield {
		"Name"
		"Number"
		"Email"
		"Phone"
	}

	filter {
		Status="Active"
	}
}

lookup Category {
	lookupobjectname:"Category"
}

lookup SubCategory {
	lookupobjectname:"SubCategory"

	displayfield {
		"Name"
		"Category"
	}

	parents {
		Category {
			parentobjectfield: "Category"
		}
	}
}

lookup Supervisor {
	lookupobjectname:"Employee"

	displayfield {
		"Name"
		"Designation"
	}

	filter {
		Designation = "Supervisor"
		Status = "Active"
	}
}

@layout
form {
	create {
		title:"Create Service Request"
		savebuttontext:"Save"
		cancelbuttontext:"Cancel"

		section {
			title:"Request Details"
			subtitle:"Basic information"

			row {
				layout:6
				fields Name
			}

			row {
				layout:12
				fields Description
			}
		}

		section {
			title:"Location"
			subtitle:"Building and unit"

			row {
				layout:6,6
				fields Building,Unit
			}

			row {
				layout:6
				fields Tenant
			}
		}

		section {
			title:"Classification"
			subtitle:"Category and priority"

			row {
				layout:6,6
				fields Category,SubCategory
			}

			row {
				layout:6
				fields Priority
			}
		}

		section {
			title:"Attachments"
			subtitle:"Upload photos"

			row {
				layout:12
				fields Photos
			}
		}
	}

	list {
		mobile {
			fields Number,Name,Building,Priority,Status
		}

		desktop {
			fields Number,Name,Building,Unit,Category,Priority,Status,Supervisor
		}
	}
}

Object with Widgets: Work Order

An object with embedded dashboard widgets for quick insights:

@object WorkOrder
@application FacilityManagement
@icon "tool"

field Number type:Autonumber format:"WO-{yy}-#" filter-order:"1" search-filter:true
field Name type:Text label:"Title"
field Type type:Select label:"Type"
field Description type:Text label:"Description" multi-line:true maximum:"2000" optional:true
field Building type:Lookup optional:true
field Priority type:Select label:"Priority" optional:true
field Status type:Select label:"Status"
field Supervisor type:Lookup label:"Supervisor" optional:true advance-search:true

select Type {
	label:"Reactive" value:"Reactive" default:true
	label:"Scheduled" value:"Scheduled"
}

select Priority {
	label:"Low" value:"Low"
	label:"Medium" value:"Medium" default:true
	label:"High" value:"High"
	label:"Critical" value:"Critical"
}

select Status {
	label:"Pending" value:"Pending" default:true
	label:"Active" value:"Active"
	label:"On Hold" value:"OnHold"
	label:"Completed" value:"Completed"
	label:"Closed" value:"Closed"
}

widgets {
	default {
		scorecard TotalWorkOrders {
			label: "Total Work Orders"
			order: 1
			query: "SELECT COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false"
		}
		scorecard ActiveWorkOrders {
			label: "Active"
			order: 2
			query: "SELECT COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE Status IN ('Pending', 'Active', 'OnHold') AND IsDeleted = false"
		}
		scorecard CompletedWorkOrders {
			label: "Completed"
			order: 3
			query: "SELECT COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE Status = 'Completed' AND IsDeleted = false"
		}
		pie WorkOrdersByStatus {
			label: "By Status"
			order: 4
			query: "SELECT Status AS label, COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Status"
		}
		pie WorkOrdersByPriority {
			label: "By Priority"
			order: 5
			query: "SELECT COALESCE(Priority, 'Not Set') AS label, COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Priority"
		}
	}
}

API Integration: JavaScript

Fetch and display contracts using the Tuli REST API:

const TULI_API = 'https://instance.tuli.io/api/v1';
const API_KEY = 'tuli_sk_live_...';

// List active contracts with pagination
async function getActiveContracts(page = 1) {
  const response = await fetch(
    `${TULI_API}/contracts?filter=Status eq 'Active'&sort=-MonthlyRent&page=${page}&pageSize=50&expand=Tenant,Building`,
    {
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      }
    }
  );
  return response.json();
}

// Response format uses string Keys (not numeric IDs)
// {
//   "data": [{
//     "Id": "contract-1CAjiqwbu2g3LkcoByCL4Y",  // Key, not integer
//     "ContractNumber": "CON-2025-00042",
//     "Tenant": {
//       "Id": "tenant-2DBkjrxcv3h4MldpCzDM5Z",
//       "Name": "Acme Corp"
//     },
//     "MonthlyRent": { "Amount": 5000, "Currency": "AED" },
//     "Status": "Active"
//   }],
//   "pagination": { "page": 1, "pageSize": 50, "totalRecords": 142 }
// }

// Create a new contract
async function createContract(data) {
  const response = await fetch(`${TULI_API}/contracts`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      Tenant: data.tenantKey,      // Use Key, not numeric ID
      Building: data.buildingKey,
      Unit: data.unitKey,
      MonthlyRent: { Amount: data.rent, Currency: 'AED' },
      StartDate: data.startDate,
      Duration: data.months,
      LeaseType: 'New'
    })
  });
  return response.json();
}

// Get single record by Key
async function getContract(key) {
  const response = await fetch(`${TULI_API}/contracts/${key}`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  });
  return response.json();
}

Standalone Dashboard

A standalone dashboard with time period filtering and multiple widget types:

@dashboard FacilityDashboard
@application FacilityManagement
@description "Facility management overview"
@time-period-filter true

scorecard TotalWorkOrders {
	label: "Total Work Orders"
	value-key: "Total"
	query: "SELECT COUNT(*) AS Total FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false"

	widget-filter {
		"toYear(CreatedAt)" = @year
	}
}

scorecard OpenWorkOrders {
	label: "Open"
	value-key: "Total"
	query: "SELECT COUNT(*) AS Total FROM wh_FacilityManagement_WorkOrder WHERE Status IN ('Pending', 'Active') AND IsDeleted = false"

	widget-filter {
		"toYear(CreatedAt)" = @year
	}
}

scorecard CompletedThisMonth {
	label: "Completed (MTD)"
	value-key: "Total"
	query: "SELECT COUNT(*) AS Total FROM wh_FacilityManagement_WorkOrder WHERE Status = 'Completed' AND IsDeleted = false AND toStartOfMonth(CreatedAt) = toStartOfMonth(today())"
}

kpicard AvgResolutionTime {
	label: "Avg Resolution"
	primary-value-key: "AvgDays"
	primary-value-format: "number"
	secondary-value-key: "TotalCompleted"
	query: "SELECT AVG(dateDiff('day', CreatedAt, ClosingTime)) as AvgDays, COUNT(*) as TotalCompleted FROM wh_FacilityManagement_WorkOrder WHERE Status = 'Completed' AND ClosingTime IS NOT NULL AND IsDeleted = false"

	widget-filter {
		"toYear(CreatedAt)" = @year
	}
}

pie WorkOrdersByStatus {
	label: "By Status"
	label-key: "Status"
	value-key: "Count"
	query: "SELECT Status AS Status, COUNT(*) AS Count FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Status"

	widget-filter {
		"toYear(CreatedAt)" = @year
	}

	group-by {
		key: "Status"
	}
}

bar MonthlyTrend {
	label: "Monthly Trend"
	x-axis-key: "Month"
	y-axis-key: "Count"
	query: "SELECT formatDateTime(CreatedAt, '%b') as Month, COUNT(*) as Count FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Month ORDER BY toMonth(CreatedAt)"

	widget-filter {
		"toYear(CreatedAt)" = @year
	}

	group-by {
		key: "formatDateTime(CreatedAt, '%b')"
	}
}

aging WorkOrderAging {
	label: "Open Request Aging"
	tint: "negative"
	query: "SELECT CASE WHEN dateDiff('day', CreatedAt, today()) <= 7 THEN '0-7 days' WHEN dateDiff('day', CreatedAt, today()) <= 30 THEN '8-30 days' WHEN dateDiff('day', CreatedAt, today()) <= 60 THEN '31-60 days' ELSE '60+ days' END AS label, COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE Status NOT IN ('Completed', 'Closed', 'Cancelled') AND IsDeleted = false GROUP BY label"
}

Workflow Example

A multi-step approval workflow:

@workflow ServiceRequestFlow
@application FacilityManagement
@module "Facility Management"

initiate {
	@model ServiceRequest

	on (submitted) {
		start "Supervisor Review"
	}
}

step "Supervisor Review" {
	@model ServiceRequest

	approvals {
		from "FM_Supervisor"
	}

	on (approved) {
		status "Scheduled"
		next "Create Work Order"
	}

	on (rejected) {
		status "Cancelled"
		finish
	}
}

step "Create Work Order" {
	@model WorkOrder

	on (creating) {
		assign "FM_Supervisor"
	}

	on (created) {
		status "InProgress"
		finish
	}
}

RBAC Example

Role definitions with row-level filtering:

@company "{company}"

role FM_Technician {
	allow ServiceRequest {
		view
	}

	allow WorkOrder {
		view {
			filter: " Supervisor = '{User.RecordId}' OR Team = '{User.RecordId}' "
		}
		update
		patch
	}

	allow WorkOrderJob {
		view {
			filter: " Team = '{User.RecordId}' "
		}
		update
		patch
	}

	allow Employee {
		view
	}

	allow Building {
		view
	}

	allow Unit {
		view
	}
}

role FM_Supervisor {
	allow FacilityManagement

	allow Employee {
		view
	}

	allow Building {
		view
	}

	allow Unit {
		view
	}

	report *
}

Field Trigger Example

Automated expiry notifications:

@object Supplier
@application Procurement

field TRNExpiryDate type:Date label:"TRN Expiry Date" optional:true

trigger TRNExpiryDate {
	intervals: [90d, 60d, 30d, 7d]
	condition: [Status == "Active"]

	task {
		assign-to: "role:ComplianceOfficer"
		subject: "TRN Expiry Review: {Name} ({Number})"
	}

	notification {
		channel: email
		template: "trn-expiry-reminder"
		to: "{ContactEmail}"
		cc: "{Supervisor.Email}"
	}
}

Experience the Platform

See how P9 and the Tuli platform work together

Ready to Build with P9?

Get hands-on with the platform. See how P9 accelerates your development workflow and integrates seamlessly with your existing systems.