REST API - Objects

CRUD endpoints, query parameters, response formats, and webhooks for the Tuli REST API.

Object API

Every P9 object automatically gets a full REST API. No additional configuration required.

Base URL

https://api.tuli.io/api/v1

Endpoints

Method Endpoint Description
GET /api/v1/{object} List records
GET /api/v1/{object}/{key} Get single record
POST /api/v1/{object} Create record
PUT /api/v1/{object}/{key} Update record
PATCH /api/v1/{object}/{key} Partial update
DELETE /api/v1/{object}/{key} Delete record

Note: All endpoints use Keys (string identifiers), not numeric IDs.

List Records

GET /api/v1/contracts?page=1&pageSize=25&sort=-CreatedAt&filter=Status eq 'Active'

Query Parameters

Parameter Type Description
page integer Page number (default: 1)
pageSize integer Records per page (default: 25, max: 100)
sort string Sort field, prefix with - for descending
filter string OData-style filter expression
fields string Comma-separated field names to include
expand string Lookup fields to expand with full records
search string Full-text search across all text fields

Response Format

{
  "Success": true,
  "Data": [
    {
      "Id": "contract-1CAjiqwbu2g3LkcoByCL4Y",
      "ContractNumber": "CON-2025-00042",
      "Tenant": {
        "Id": "tenant-2DBkjrxcv3h4MldpCzDM5Z",
        "Name": "Acme Corp"
      },
      "MonthlyRent": {
        "Amount": 5000,
        "Currency": "AED"
      },
      "Status": "Active",
      "CreatedAt": "2025-01-15T10:30:00Z",
      "ModifiedAt": "2025-02-01T14:20:00Z"
    }
  ],
  "Pagination": {
    "Page": 1,
    "PageSize": 25,
    "TotalRecords": 142,
    "TotalPages": 6
  }
}

Get Single Record

GET /api/v1/contracts/contract-1CAjiqwbu2g3LkcoByCL4Y

Response:

{
  "Success": true,
  "Data": {
    "Id": "contract-1CAjiqwbu2g3LkcoByCL4Y",
    "ContractNumber": "CON-2025-00042",
    "Tenant": {
      "Id": "tenant-2DBkjrxcv3h4MldpCzDM5Z",
      "Name": "Acme Corp"
    },
    "Building": {
      "Id": "building-3ECljsydw4i5NmeqDaEN6A",
      "Name": "Tower A"
    },
    "Unit": {
      "Id": "unit-4FDmktzex5j6OlfpEbFO7B",
      "Number": "1201"
    },
    "MonthlyRent": {
      "Amount": 5000,
      "Currency": "AED"
    },
    "StartDate": "2025-03-01",
    "EndDate": "2027-02-28",
    "Duration": 24,
    "Status": "Active",
    "CreatedAt": "2025-01-15T10:30:00Z",
    "ModifiedAt": "2025-02-01T14:20:00Z"
  }
}

Create Record

POST /api/v1/contracts
Content-Type: application/json

{
  "Tenant": "tenant-2DBkjrxcv3h4MldpCzDM5Z",
  "Building": "building-3ECljsydw4i5NmeqDaEN6A",
  "Unit": "unit-4FDmktzex5j6OlfpEbFO7B",
  "MonthlyRent": {
    "Amount": 5000,
    "Currency": "AED"
  },
  "StartDate": "2025-03-01",
  "Duration": 24,
  "LeaseType": "New"
}

Response:

{
  "Success": true,
  "Data": {
    "Id": "contract-5GEnluafy6k7PmgqFcGP8C",
    "ContractNumber": "CON-2025-00043",
    "Status": "Draft"
  }
}

Update Record

PUT /api/v1/contracts/contract-1CAjiqwbu2g3LkcoByCL4Y
Content-Type: application/json

{
  "MonthlyRent": {
    "Amount": 5500,
    "Currency": "AED"
  },
  "Status": "Active"
}

Partial Update

PATCH /api/v1/contracts/contract-1CAjiqwbu2g3LkcoByCL4Y
Content-Type: application/json

{
  "Status": "Active"
}

Delete Record

DELETE /api/v1/contracts/contract-1CAjiqwbu2g3LkcoByCL4Y

Error Handling

{
  "Success": false,
  "Error": {
    "Code": "VALIDATION_ERROR",
    "Message": "Validation failed",
    "Details": [
      { "Field": "MonthlyRent", "Message": "MonthlyRent is required" },
      { "Field": "StartDate", "Message": "StartDate must be in the future" }
    ]
  }
}
Code Status Description
VALIDATION_ERROR 400 Request body validation failed
UNAUTHORIZED 401 Missing or invalid authentication
FORBIDDEN 403 Insufficient permissions
NOT_FOUND 404 Record not found
CONFLICT 409 Duplicate or conflict
RATE_LIMITED 429 Too many requests
SERVER_ERROR 500 Internal server error

Webhooks

Register webhooks to receive real-time notifications:

POST /api/v1/webhooks
Content-Type: application/json

{
  "Url": "https://your-app.com/webhook",
  "Events": [
    "contract.created",
    "contract.updated",
    "contract.workflow.approved"
  ],
  "Secret": "your-webhook-secret"
}

Webhook payload:

{
  "Event": "contract.created",
  "Timestamp": "2025-03-15T10:30:00Z",
  "Data": {
    "Id": "contract-1CAjiqwbu2g3LkcoByCL4Y",
    "ContractNumber": "CON-2025-00042",
    "Status": "Draft"
  }
}

Workflow Actions

Trigger workflow actions on records:

POST /api/v1/contracts/contract-1CAjiqwbu2g3LkcoByCL4Y/workflow/submit
POST /api/v1/contracts/contract-1CAjiqwbu2g3LkcoByCL4Y/workflow/approve
Content-Type: application/json

{
  "Comment": "Approved for processing"
}

Search & Filtering

Full-Text Search

GET /api/v1/contracts?search=acme

Filter Expressions (OData-style)

# Equals
GET /api/v1/contracts?filter=Status eq 'Active'

# Not equals
GET /api/v1/contracts?filter=Status ne 'Draft'

# Greater than
GET /api/v1/contracts?filter=MonthlyRent gt 5000

# Contains (text)
GET /api/v1/contracts?filter=contains(Tenant/Name, 'Acme')

# Multiple conditions
GET /api/v1/contracts?filter=Status eq 'Active' and MonthlyRent gt 5000

Expanding Lookups

Include related record data:

GET /api/v1/contracts?expand=Tenant,Building,Unit

Response includes full lookup data:

{
  "Id": "contract-1CAjiqwbu2g3LkcoByCL4Y",
  "Tenant": {
    "Id": "tenant-2DBkjrxcv3h4MldpCzDM5Z",
    "Name": "Acme Corp",
    "Email": "contact@acme.com",
    "Phone": "+971501234567"
  }
}

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.