Facilio REST API v5

Programmatic access to Facilio's Connected CMMS — work orders, service requests, assets, portfolio hierarchy (sites, buildings, floors, spaces), people (tenants, vendors, clients), inventory, and purchasing. REST/JSON, OpenAPI 3.0.3, API-key or OAuth2 auth, regional hosts.

OpenAPI Specification

facilio-openapi-original.yaml Raw ↑
openapi: 3.0.3
info:
  title: Facilio REST API
  version: 5.0.0
  description: |
    The Facilio REST API gives you programmatic access to Facilio's Connected CMMS — the unified platform for managing property operations at portfolio scale.

    Build integrations that connect Facilio with your ERP, accounting systems, BMS, IoT platforms, and other business tools. Automate work order creation from external triggers, synchronize asset data across systems, push tenant and vendor records from your CRM, or pull maintenance data into your reporting dashboards.

    **What you can do with this API:**
    - **Work Orders & Service Requests** — Create, assign, track, and close maintenance tasks programmatically
    - **Assets** — Manage your equipment registry, track asset lifecycle, and sync with external asset management systems
    - **Portfolio (Sites, Buildings, Floors, Spaces)** — Maintain your facility hierarchy and space data
    - **People (Tenants, Vendors, Clients)** — Synchronize contacts and stakeholder records
    - **Inventory (Item Types, Tool Types, Items & Tools, Services, Storerooms)** — Master data, per-storeroom item/tool balances (read), per-bin quantities by item or tool record, quantity adjustments, and warehouse locations
    - **Procurement (Quotes, Purchase Requests, Purchase Orders, Receivable receiving, Invoices)** — Procurement with line items; receive against a PO via receivable APIs
    - **Credit Notes (Vendor Credits, Client Credits)** — Manage credit notes for refunds and transaction adjustments with line items
    - **Inventory Requests** — Track material requisitions from work orders to storerooms
    - **Custom Modules** — Manage your organization's custom modules — record types you define for the data and workflows that are specific to your business
    - **Schema Discovery** — List all accessible modules and inspect field schemas (name, type, required, readOnly, lookup target) via `GET /modules` and `GET /{moduleName}/metadata`
    - **Attachments & Comments** — Attach documents, photos, and notes to work orders and service requests
    - **Picklists** — Discover available values for status, priority, category, and other configurable fields

    The API follows REST conventions with JSON request/response bodies, standard HTTP methods (GET, POST, PATCH, DELETE), and consistent error handling across all endpoints.

    ## Base URL

    ```
    https://{region}.facilioapis.com/{app_name}/api/v5
    ```

    | Variable | Description | Values |
    |----------|-------------|--------|
    | `region` | Your deployment region | `us`, `au`, `ae`, `uk`, `us-azure`, `sa` |
    | `app_name` | `maintenance` for API Key auth, `developer` for OAuth2 auth | `maintenance`, `developer` |

    ## Authentication

    All API requests must be authenticated using one of the following methods:

    ### API Key (Personal Access Token)
    Generate an API key from your Facilio account settings and pass it in the `x-api-key` header.
    The key inherits the permissions of the user who created it.

    ```
    x-api-key: your-api-key-here
    ```

    ### OAuth2
    For developer app integrations. Supports **authorization_code** and **password** grant types.

    | Endpoint | URL |
    |----------|-----|
    | Authorize | `https://{region}.facilioapis.com/identity/oauth2/authorize` |
    | Token | `https://{region}.facilioapis.com/identity/oauth2/token` |
    | Refresh | `https://{region}.facilioapis.com/identity/oauth2/token` (with `grant_type=refresh_token`) |
    | Revoke | `https://{region}.facilioapis.com/identity/oauth2/revoke` |

    Pass the access token as:
    ```
    Authorization: Bearer oauth2 <access_token>
    ```

    ---

    ## List Operations

    All list endpoints (`GET /{module}`) support these query parameters:

    | Parameter | Type | Default | Description |
    |-----------|------|---------|-------------|
    | `page` | integer | 1 | Page number (1-based) |
    | `pageSize` | integer | 50 | Number of records per page. Maximum is 200. |
    | `select` | string | — | Comma-separated field names to include in the response. If omitted, module default fields are returned. |
    | `expand` | string | — | Comma-separated lookup field names to expand with full details (max 5). By default, lookup fields return only `{id}`. |
    | `search` | string | — | Free-text search on the module's primary field (e.g. subject for work orders, name for assets). |
    | `sortBy` | string | — | Field name to sort by. Only sortable fields are accepted (see each module's documentation). |
    | `sortOrder` | string | `desc` | Sort direction: `asc` (ascending) or `desc` (descending). |
    | `count` | boolean | false | When `true`, the response includes a `count` field with the total number of matching records. |
    | `view` | string | — | Name of a saved view. Applies the view's columns, filters, and sort order. |
    | `changed` | string | — | UTC timestamp for delta sync (format: `yyyy-MM-dd'T'HH:mm:ss'Z'`). Returns records created or modified after this time. |

    ---

    ## Filtering

    Apply filters by adding query parameters to any list endpoint. Filters are combined with AND logic.

    **Exact match:**
    ```
    GET /workorder?status=Submitted&priority=High
    ```

    **Operator match:**
    ```
    GET /workorder?subject(contains)=HVAC&createdTime(after)=2026-01-01T00:00:00Z
    ```

    ### Operators by Field Type

    | Field Type | Example Fields | Operators | Value Format |
    |------------|----------------|-----------|--------------|
    | String | subject, name, description | `is`, `isn_t`, `contains`, `doesn_t_contain`, `starts_with`, `ends_with` | Text string |
    | Number | serialNumber, area, noOfTasks | `equals`, `not_equals`, `greater_than`, `less_than`, `greater_than_equal`, `less_than_equal`, `between` | Number (for `between`: two comma-separated values) |
    | Date/DateTime | createdTime, dueDate, scheduledStart | `is`, `isn_t`, `before`, `after`, `between`, `today`, `yesterday`, `current_week`, `last_week`, `current_month`, `last_month` | UTC `yyyy-MM-dd'T'HH:mm:ss'Z'` (value-less operators like `today` need no value) |
    | Boolean | highRisk, decommission | `equals`, `is` | `true` or `false` |
    | Lookup | resource, site, assignedTo, createdBy | `is`, `isn_t` | Record ID (integer) |
    | Picklist | status, priority, category, type | `is`, `isn_t` | Display name string (e.g. `Submitted`) or ID |
    | Enum | sourceType, urgency | `is`, `isn_t` | Display name string or index |
    | All types | Any field | `is_empty`, `is_not_empty` | No value needed |

    **Multiple values (OR within a field):**
    ```
    GET /workorder?priority=High,Medium
    ```

    ---

    ## Delta Sync

    For incremental data synchronization, use the `changed` parameter to fetch only records that have been created or modified since your last sync:

    ```
    GET /workorder?changed=2026-02-01T00:00:00Z
    ```

    This checks both the created time and modified time of each record (OR condition), so you get both new and updated records. You can combine `changed` with other filters.

    ---

    ## Custom Fields

    Custom fields are user-defined fields added via Facilio setup. They follow the naming pattern `{field_name}_{module}` (e.g. `po_reference_workorder`, `client_type_site`, `warranty_info_asset`).

    | Operation | Behavior |
    |-----------|----------|
    | **Single record GET** | Custom fields are automatically included in the response |
    | **List GET** | Custom fields are excluded by default. Use `?select=subject,po_reference_workorder` to include specific ones |
    | **Create / Update** | Pass custom fields in the request body alongside system fields |
    | **Filtering** | Custom fields can be used as filter parameters |
    | **Sorting** | Custom fields of primitive types (string, number, date) can be used with `sortBy` |

    > **Note:** Large text (BIG_STRING) custom fields are always excluded from list responses to prevent excessive memory usage. They are only available on single record GET.

    **Example — Create a work order with custom fields:**
    ```json
    {
      "data": {
        "subject": "HVAC Repair",
        "siteId": 10,
        "po_reference_workorder": "PO-2026-001",
        "cost_estimate_workorder": 1500.00
      }
    }
    ```

    ---

    ## Lookup fields in responses

    A **lookup** links your record to another record or to a controlled list (site, assignee, tenant, status, and so on). The JSON shape depends on which endpoint you call.

    ### Lists (`GET /{module}`)

    By default, each lookup is compact: `{ "id": <number> }`.

    To load more detail on specific lookups, use `?expand=` with a comma-separated list of **lookup field names** (maximum **5** fields).

    ### Single record, create, and update

    On **GET** by id, **POST** create, and **PATCH** update, lookups that appear in the response are usually **expanded** into a small object instead of only an id.

    What you get is decided by **what the field points to**:

    - **Standard business records** in this API (for example site, tenant, asset, work order): the fields that module normally exposes, **including custom fields** you added on that target record. If the expanded object itself contains another lookup, that inner lookup stays compact: `{ "id" }` only.
    - **People and shared reference data** (for example `users`, `people`, `location`, `resource`): a **short, fixed list** of fields the API publishes — e.g. users typically include `id`, `name`, `email`, and `phone` when present. Internal database columns are not returned.
    - **Your org’s custom module** as the target: that module’s usual fields **plus** its custom fields.
    - **Any other target** not covered above: **`id`** and **`name`** only.

    Some fields behave as **picklists** (status, priority, category, type, …). They often return a **single text or id value** (e.g. `"Submitted"`) instead of a nested object. Check `GET /{moduleName}/metadata` for the field’s `dataType` and picklist details.

    Properties that are null are omitted from the JSON body.

    ---

    ## Response Format

    **Success — single record:**
    ```json
    {
      "success": true,
      "data": { "id": 1, "subject": "WO-1", "status": "Submitted" }
    }
    ```

    **Success — list:**
    ```json
    {
      "success": true,
      "data": [ { "id": 1, ... }, { "id": 2, ... } ],
      "pagination": { "page": 1, "pageSize": 50 },
      "count": 120
    }
    ```

    **Error:**
    ```json
    {
      "success": false,
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "A specified field does not exist or is not accessible"
      }
    }
    ```

    ---

    ## Error Codes

    | Code | HTTP | Description |
    |------|------|-------------|
    | `UNAUTHORIZED` | 401 | Missing or invalid authentication |
    | `FORBIDDEN` | 403 | Insufficient permissions for this operation |
    | `MODULE_NOT_FOUND` | 404 | Module does not exist or is not accessible in this app |
    | `RECORD_NOT_FOUND` | 404 | Record with the given ID was not found |
    | `API_NOT_FOUND` | 404 | The API endpoint does not exist |
    | `VIEW_NOT_FOUND` | 404 | The specified saved view was not found |
    | `PICKLIST_NOT_FOUND` | 404 | The specified picklist field was not found |
    | `VALIDATION_ERROR` | 400 | Request body or parameters failed validation |
    | `INVALID_FIELD` | 400 | A specified field does not exist or is not accessible |
    | `INVALID_FILTER` | 400 | Filter syntax is invalid or operator not supported for field type |
    | `EXPAND_LIMIT_EXCEEDED` | 400 | More than 5 fields specified in `expand` parameter |
    | `METHOD_NOT_ALLOWED` | 405 | HTTP method not supported for this endpoint |
    | `CREATE_NOT_ALLOWED` | 403 | Create operation is disabled for this module |
    | `UPDATE_NOT_ALLOWED` | 403 | Update operation is disabled for this module |
    | `DELETE_NOT_ALLOWED` | 403 | Delete operation is disabled for this module |
    | `MODULE_NOT_ENABLED` | 403 | Module is not enabled for this organization |
    | `RATE_LIMITED` | 429 | Rate limit exceeded |
    | `INTERNAL_ERROR` | 500 | Unexpected server error |

    ---

    ## Rate Limiting

    - **Limit:** 100 requests per minute per API key / OAuth2 token
    - **Response:** HTTP 429 Too Many Requests when exceeded
    - **Recommendation:** Implement exponential backoff when receiving 429 responses

    ---

    ## HTTP Method Override

    Some systems only support POST and GET requests. To use PATCH, or DELETE through a POST request,
    include the `X-HTTP-Method-Override` header with the desired method.

    ```
    POST /workorder/13
    X-HTTP-Method-Override: PATCH
    Content-Type: application/json

    { "data": { "priority": "Low" } }
    ```

    This is equivalent to `PATCH /workorder/13`. Supported override values: `PATCH`, `DELETE`.
    The override header is only honored on POST requests.

    ---

    ## Field Types and Limits

    | Field Type | Max Length | Description |
    |------------|-----------|-------------|
    | String | 255 characters | Short text fields (e.g. subject, name, email) |
    | Large Text | 2,000 characters | Medium text fields (e.g. description) |
    | Big String | 32,000 characters | Long text fields (custom large text fields). Excluded from list API responses. |
    | Number | — | Integer or decimal values |
    | Date / DateTime | — | UTC format: `yyyy-MM-dd'T'HH:mm:ss'Z'` |
    | Boolean | — | `true` or `false` |
    | Lookup | — | Reference to another record. Pass the record ID on create/update. Returns `{id}` on list unless `?expand=` is used. On single-record GET (and typical create/update responses), expanded shape follows **Lookup fields in responses** above — not a single fixed `{id, name, email}` for every field. |
    | Picklist | — | Enumerated values (status, priority, category, type). Pass display name string (e.g. `"High"`) or numeric ID. |
    | Enum | — | System enum values. Pass display name string or index. |

    Values exceeding the maximum length will be rejected with a `VALIDATION_ERROR`.

    ---

    ## Additional Notes

    - **Skipping Workflows:** By default, workflow rules (automations, notifications, approvals) execute when you create or update a record. To skip them, pass the header `X-Execute-Workflows: false`. This is useful for bulk data imports or migrations where triggering automations is not desired.
    - **Picklist Fields:** Fields like status, priority, category, and type accept and return display name strings (e.g. `"Submitted"`, `"High"`, `"Energy"`). You can also pass the numeric ID.
    - **Lookup Fields:** On list API, lookup fields return `{id}` only unless `?expand=` is used. On single record GET (and create/update responses), lookups are expanded automatically; the property set depends on the lookup target module — see **Lookup fields in responses**.

  contact:
    name: Facilio Support
    url: https://facilio.com
  license:
    name: Proprietary

servers:
  - url: https://{region}.facilioapis.com/{app_name}/api/v5
    variables:
      region:
        description: Regional deployment
        default: us
        enum: [us, au, ae, uk, us-azure, sa]
      app_name:
        description: "'maintenance' for API Key, 'developer' for OAuth2"
        default: maintenance
        enum: [maintenance, developer]

security:
  - apiKey: []
  - oauth2: []

tags:
  - name: Common
    description: User profile and utility endpoints
  - name: Work Orders
    description: Work orders represent maintenance tasks, repairs, and scheduled jobs across your facilities. Track them from creation through assignment, execution, and closure. Assign to staff or teams, set priorities and due dates, and monitor progress.
  - name: Work Order Attachments
    description: Attach photos, documents, invoices, and other files to work orders. Useful for before/after photos, inspection reports, and supporting documentation.
  - name: Work Order Comments
    description: Add notes and updates to work orders. Comments provide an audit trail of communication between technicians, managers, and requesters.
  - name: Service Requests
    description: Service requests capture facility issues reported by occupants, tenants, or staff. They typically flow through triage, assignment, and resolution — and can be converted into work orders when maintenance action is needed.
  - name: Service Request Attachments
    description: Attach photos and documents to service requests, such as screenshots of issues or supporting evidence from reporters.
  - name: Service Request Comments
    description: Add notes and status updates to service requests to keep requesters and assignees informed.
  - name: Assets
    description: Assets represent the equipment, machines, and devices in your facilities — HVAC units, elevators, generators, fire panels, and more. Track their lifecycle, location, warranty, and maintenance history.
  - name: Sites
    description: Sites are the top-level locations in your portfolio — campuses, office buildings, warehouses, or any physical facility you manage. Each site can contain buildings, floors, and spaces.
  - name: Buildings
    description: Buildings belong to a site and represent individual structures. Track floor count, area, and contacts for each building.
  - name: Floors
    description: Floors belong to a building and represent individual levels. Track floor level, area, and associated spaces.
  - name: Spaces
    description: Spaces are the rooms, zones, and areas within your buildings — conference rooms, lobbies, server rooms, parking areas, and more. Track occupancy, area, and category.
  - name: Clients
    description: Clients are the organizations you provide facility services to. Manage their contact information and associate them with sites and work orders.
  - name: Client Contacts
    description: Individual contacts within a client organization. Manage names, emails, and phone numbers.
  - name: Vendors
    description: Vendors are the external service providers and contractors who perform work at your facilities. Manage their contact details and associate them with work orders.
  - name: Vendor Contacts
    description: Individual contacts within a vendor organization.
  - name: Tenants
    description: Tenants are the organizations or individuals who occupy space in your facilities. Manage lease-related contacts, tenant types, and unit assignments.
  - name: Tenant Contacts
    description: Individual contacts within a tenant organization.
  - name: Tenant Units
    description: Manage tenant unit (space) assignments.
  - name: Item Types
    description: Consumable materials in your inventory (filters, bulbs, fasteners). Manage costing, reorder thresholds, and pricing.
  - name: Tool Types
    description: Reusable equipment tracked in your inventory (drills, multimeters, ladders). Manage quantities, pricing, and approvals.
  - name: Services
    description: Labor and service offerings (cleaning, inspection, calibration). Define buying/selling prices, duration, and payment type.
  - name: Storerooms
    description: Warehouse locations where inventory is stored. Each storeroom belongs to a site and can serve multiple sites.
  - name: Quotes
    description: Pricing proposals with line items. Line items on PATCH represent the full desired state — omitted items are deleted.
  - name: Invoices
    description: Billing records with line items, typically created from quotes. Track costs, taxes, and approval status.
  - name: Purchase Requests
    description: Internal requisitions for materials or services, with line items. Line items on PATCH represent the full desired state.
  - name: Purchase Orders
    description: Vendor purchase orders with line items; track ordered and received quantities.
  - name: Receivables
    description: One receiving record per purchase order; list, read, and drive receipts through action endpoints.
  - name: Items
    description: Item balances per storeroom (read-only list, detail, and bin list); non-rotating quantity changes use the item adjustment endpoint.
  - name: Tools
    description: Tool balances per storeroom (read-only list, detail, and bin list); non-rotating quantity changes use the tool adjustment endpoint.
  - name: Inventory Requests
    description: Material requisitions linked to work orders. Request items or tools from a storeroom.
  - name: Vendor Credits
    description: Vendor credit notes for refunds and transaction adjustments. Track credit amounts, line items, and approval status for vendor-related credits.
  - name: Client Credits
    description: Client credit notes for refunds and transaction adjustments. Track credit amounts, line items, and approval status for client-related credits.
  - name: Custom Modules
    description: Manage your organization's custom modules — record types you define for the data and workflows that are specific to your business.
  - name: Picklists
    description: |
      Retrieve picklist values for enum and system lookup fields.
      Use these to discover valid values for fields like status, priority, category, and type.
      Custom picklist fields can also be queried using `GET /picklist/{module}/{fieldName}`.
paths:
  # ════════════════════════════════════════════════════════════════
  # Common
  # ════════════════════════════════════════════════════════════════

  /profile:
    get:
      tags: [Common]
      summary: Get current user profile
      description: Returns the authenticated user's profile information including name, email, timezone, role, and organization details.
      operationId: getProfile
      responses:
        '200':
          description: User profile retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/Profile'
              example:
                success: true
                data:
                  user:
                    id: 1
                    name: "Alex Johnson"
                    email: "alex.johnson@example.com"
                    timezone: "Asia/Kolkata"
                    role:
                      id: 10
                      name: "Administrator"
                  org:
                    id: 100
                    name: "Facilio Inc"
                    domain: "facilio"
        '401':
          $ref: '#/components/responses/Unauthorized'

  /modules:
    get:
      tags: [Common]
      summary: List all accessible modules
      description: |
        Returns a catalogue of every module accessible via the V5 API for the current org:

        - **System modules** — standard Facilio modules available in every org (workorder, asset, site, etc.)
        - **Custom modules** — modules created by your organization via Facilio Setup

        Use this endpoint to discover which module names are valid for other V5 API calls, and to determine whether a given module is a system module or an org-created custom module.

        Pass `?custom=true` to scope the response to only custom modules created by your organization. The default response returns both system and custom modules.

        Each entry includes a `description` field when one is configured for the module.
      operationId: listModules
      parameters:
        - name: custom
          in: query
          description: |
            When `true`, return only org-created custom modules (modules where `isCustom = true`).
            Default `false` (or absent) returns the full system + custom catalogue — this preserves the previous default behavior.
          schema: {type: boolean, default: false}
      responses:
        '200':
          description: Module catalogue retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FacilioModule'
              example:
                success: true
                data:
                  - name: workorder
                    displayName: "Work Orders"
                    description: "Standard work order tracking"
                    isCustom: false
                  - name: asset
                    displayName: "Assets"
                    description: "Equipment and asset records"
                    isCustom: false
                  - name: custom_employees
                    displayName: "Employees"
                    description: "Custom employee records"
                    isCustom: true
                  - name: custom_projects
                    displayName: "Projects"
                    isCustom: true
        '401':
          $ref: '#/components/responses/Unauthorized'


  # ════════════════════════════════════════════════════════════════
  # Work Orders
  # ════════════════════════════════════════════════════════════════

  /workorder:
    get:
      tags: [Work Orders]
      summary: List work orders
      description: |
        Returns a paginated list of work orders. Supports filtering, sorting, field selection, and saved views.
        By default, returns module-configured fields. Use `?select=` to customize.
        Custom fields can be included via `?select=subject,po_reference_workorder`.
      operationId: listWorkOrders
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - $ref: '#/components/parameters/select'
        - $ref: '#/components/parameters/expand'
        - $ref: '#/components/parameters/search'
        - $ref: '#/components/parameters/count'
        - $ref: '#/components/parameters/view'
        - $ref: '#/components/parameters/changed'
        - name: sortBy
          in: query
          description: Field to sort by (only sortable fields accepted)
          schema:
            type: string
            enum: [id, serialNumber, subject, actualWorkDuration, actualWorkStart, actualWorkEnd, estimatedWorkDuration, scheduledStart, estimatedEnd, responseDueDate, dueDate, noOfAttachments, noOfClosedTasks, noOfNotes, noOfTasks, createdTime, modifiedTime]
        - name: sortOrder
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
      responses:
        '200':
          description: Paginated list of work orders
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ListResponse'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/WorkOrder'
              example:
                success: true
                data:
                  - id: 13
                    serialNumber: 13
                    subject: "HVAC Repair - Building A"
                    status: "Submitted"
                    priority: "High"
                    category: "Energy"
                    type: "Corrective"
                    sourceType: "Web Work Order"
                    siteId: {id: 10}
                    resource: {id: 14}
                    scheduledStart: "2026-02-12T16:27:24Z"
                    noOfAttachments: 0
                    createdTime: "2026-02-12T16:27:24Z"
                    createdBy: {id: 1}
                    modifiedTime: "2026-02-12T16:27:24Z"
                  - id: 14
                    serialNumber: 14
                    subject: "Plumbing leak in Floor 2"
                    status: "Work in Progress"
                    priority: "Medium"
                    category: "Plumbing"
                    createdTime: "2026-02-13T09:15:00Z"
                    createdBy: {id: 2}
                pagination:
                  page: 1
                  pageSize: 50
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Work Orders]
      summary: Create a work order
      description: |
        Creates a new work order. Requires `subject` and `site` at minimum.
        Picklist fields (priority, category, type) accept display name strings (e.g. `"High"`) or numeric IDs.
        Custom fields can be passed alongside system fields.
      operationId: createWorkOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [data]
              properties:
                data:
                  $ref: '#/components/schemas/WorkOrder'
            example:
              data:
                subject: "HVAC Repair - Building A"
                siteId: 10
                priority: "High"
                category: "Energy"
                description: "AC unit not cooling properly on Floor 3"
                assignedTo: 5
                dueDate: "2026-02-15T17:00:00Z"
                po_reference_workorder: "PO-2026-001"
      responses:
        '201':
          description: Work order created successfully
          content:
            application/json:
              example:
                success: true
                data:
                  id: 15
                  serialNumber: 15
                  subject: "HVAC Repair - Building A"
                  status: "Submitted"
                  priority: "High"
                  category: "Energy"
                  siteId: {id: 10, name: "Headquarters"}
                  createdTime: "2026-02-14T10:00:00Z"
                  createdBy: {id: 1, name: "Alex Johnson", email: "alex.johnson@example.com"}
                message: "Record created"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /workorder/{id}:
    get:
      tags: [Work Orders]
      summary: Get a work order
      description: |
        Returns a single work order with all system fields, custom fields, and expanded lookup fields.
        Lookup expansion follows **Lookup fields in responses** in the API overview.
      operationId: getWorkOrder
      parameters:
        - $ref: '#/components/parameters/recordId'
        - $ref: '#/components/parameters/select'
        - $ref: '#/components/parameters/expand'
      responses:
        '200':
          description: Work order details
          content:
            application/json:
              example:
                success: true
                data:
                  id: 13
                  serialNumber: 13
                  subject: "HVAC Repair - Building A"
                  description: "AC unit not cooling properly on Floor 3"
                  status: "Submitted"
                  priority: "High"
                  category: "Energy"
                  type: "Corrective"
                  sourceType: "Web Work Order"
                  resource: {id: 14, name: "AHU-01"}
                  siteId: {id: 10, name: "Headquarters"}
                  assignedTo: {id: 5, name: "John Smith", email: "john@facilio.com"}
                  tenant: {id: 90, name: "TechStart Inc"}
                  scheduledStart: "2026-02-12T16:27:24Z"
                  dueDate: "2026-02-15T17:00:00Z"
                  noOfAttachments: 2
                  noOfTasks: 3
                  createdTime: "2026-02-12T16:27:24Z"
                  createdBy: {id: 1, name: "Alex Johnson", email: "alex.johnson@example.com"}
                  modifiedTime: "2026-02-13T09:30:00Z"
                  po_reference_workorder: "PO-2026-001"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags: [Work Ord

# --- truncated at 32 KB (313 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/facilio/refs/heads/main/openapi/facilio-openapi-original.yaml