{
  "openapi": "3.1.0",
  "info": {
    "title": "Laksana Messaging API",
    "version": "1.0.0",
    "description": "Meta WhatsApp (and future Telegram) webhook ingress that creates work updates and issues while exposing channel, organisation, site, and facility management endpoints. CMMS Inventory is currently managed in the admin app; API-driven work-order completion records consumed parts through the inventory ledger when the organisation has the cmms_inventory entitlement."
  },
  "servers": [
    {
      "url": "https://api.laksana.app",
      "description": "Production server"
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Authentication endpoints"
    },
    {
      "name": "Inbox",
      "description": "Unified inbox ingestion and conversion to Work Updates or Issues."
    },
    {
      "name": "Webhooks",
      "description": "WhatsApp/Telegram callbacks"
    },
    {
      "name": "WhatsApp",
      "description": "Programmatic Meta WhatsApp actions (media, groups, participants)"
    },
    {
      "name": "Channels",
      "description": "Group or direct messaging channels with behaviour rules"
    },
    {
      "name": "Locations & Assets",
      "description": "Operational locations and tracked assets/units"
    },
    {
      "name": "Messaging Accounts",
      "description": "BYON WhatsApp numbers and Telegram bots"
    },
    {
      "name": "Organisations",
      "description": "Tenant configuration and metadata"
    },
    {
      "name": "CMMS Inventory",
      "description": "CMMS inventory over the API (inventory-ops blueprint 20260818, W7.3): reads, exact barcode lookup, stock verbs and stock-take sessions. Item create/update/delete stay console-only by design — the admin UI owns master data, the plan cap and hierarchy rules."
    },
    {
      "name": "Mobile",
      "description": "Mobile endpoints for dashboard, work updates, problems, and chat"
    },
    {
      "name": "BYON",
      "description": "Bring Your Own Number WhatsApp account management"
    },
    {
      "name": "Knowledge Base",
      "description": "Knowledge base search and suggestion endpoints"
    },
    {
      "name": "Notification Preferences",
      "description": "User notification preference management"
    },
    {
      "name": "Issues",
      "description": "Issue and SLA management endpoints"
    },
    {
      "name": "AI Assistant",
      "description": "AI-powered assistant query endpoint"
    },
    {
      "name": "Health",
      "description": "Health check endpoints for monitoring"
    },
    {
      "name": "Pricing",
      "description": "Public pricing information endpoint"
    },
    {
      "name": "Compliance",
      "description": "Compliance check and verification management"
    },
    {
      "name": "Reporting",
      "description": "Reporting and analytics endpoints"
    },
    {
      "name": "Notifications",
      "description": "Notification and device token management"
    },
    {
      "name": "Work Orders",
      "description": "Part W — a member's work orders for laksana-mobile: list, read and move, with the console's scope and rules."
    }
  ],
  "paths": {
    "/api/health/ping": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Health check ping",
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/health/whatsapp": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "WhatsApp API health check",
        "responses": {
          "200": {
            "description": "WhatsApp API connectivity status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "whatsapp_api": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/health/whatsapp-bot": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "WhatsApp bot health check",
        "responses": {
          "200": {
            "description": "WhatsApp bot health status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pricing": {
      "get": {
        "tags": [
          "Pricing"
        ],
        "summary": "Get public pricing information",
        "responses": {
          "200": {
            "description": "Pricing plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PricingPlan"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/organisations": {
      "get": {
        "tags": [
          "Organisations"
        ],
        "summary": "List organisations",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged organisations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedOrganisations"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Organisations"
        ],
        "summary": "Create an organisation",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrganisationInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Organisation created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organisation"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/organisations/{organisationId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/OrganisationId"
        }
      ],
      "get": {
        "tags": [
          "Organisations"
        ],
        "summary": "Get organisation",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Organisation record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organisation"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Organisations"
        ],
        "summary": "Update organisation defaults",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrganisationInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organisation updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organisation"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6): `member_required` — an integration key without the `act_as_creator` scope; `permission_denied` — the member (or, for an `act_as_creator` key, its creator) lacks `administration.organisation.manage`; `organisation_required` — a token with no organisation. Another workspace's id answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed, or (Task H6) `platform_field_forbidden`: a workspace credential tried to change `status` or `settings.limits` — platform fields only the master key sets.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PlatformFieldRefusal"
                    },
                    {
                      "type": "object"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/messaging-accounts": {
      "get": {
        "tags": [
          "Messaging Accounts"
        ],
        "summary": "List messaging accounts",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged messaging accounts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedMessagingAccounts"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Messaging Accounts"
        ],
        "summary": "Register a BYON WhatsApp number or Telegram bot",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessagingAccountInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Messaging account created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagingAccount"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.messaging_accounts.create` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Task H6 review: `phone_number_id_unavailable` — the platform's own phone number id, or one another organisation holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberIdRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/messaging-accounts/{messagingAccountId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessagingAccountId"
        }
      ],
      "get": {
        "tags": [
          "Messaging Accounts"
        ],
        "summary": "Get messaging account",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagingAccount"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Messaging Accounts"
        ],
        "summary": "Update status or metadata",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessagingAccountInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagingAccount"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.messaging_accounts.edit` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Task H6 review: `phone_number_id_unavailable` — the platform's own phone number id, or one another organisation holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberIdRefusal"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Messaging Accounts"
        ],
        "summary": "Delete a messaging account",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Account removed"
          },
          "403": {
            "description": "Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.messaging_accounts.delete` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/channels": {
      "get": {
        "tags": [
          "Channels"
        ],
        "summary": "List channels",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged channels",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedChannels"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Channels"
        ],
        "summary": "Create or reconfigure a channel",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Channel created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation. Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.channels.create` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/channels/{channelId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ChannelId"
        }
      ],
      "get": {
        "tags": [
          "Channels"
        ],
        "summary": "Get a channel",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Channel record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Channels"
        ],
        "summary": "Update channel behaviour or status",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Channel updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation. Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.channels.edit` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Channels"
        ],
        "summary": "Archive a channel",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Channel archived"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation. Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.channels.delete` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sites": {
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "List locations",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedLocations"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "parent_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "archived"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_children",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "When parent_id is provided, include the parent and all descendants instead of direct children only."
          }
        ]
      },
      "post": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Create a location",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocationInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Location created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sites/{siteId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LocationId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Get a location",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Location record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Update location metadata",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocationInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Location updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Archive a location",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Location archived"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilities": {
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "List assets",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "site_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "parent_facility_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "include_children",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "archived"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged assets",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedThings"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Create an asset",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThingInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Asset created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6): `permission_denied` — the member lacks `operations.facilities.create` / `operations.facilities.manage` (for an integration key, the admin who minted it). A foreign asset answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilities/search": {
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Search assets, units, spaces, and sub-assets",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "site_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching assets",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AssetSearchResult"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilities/{facilityId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ThingId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Get an asset",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Asset record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Update asset metadata",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThingInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Asset updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6): `permission_denied` — the member lacks `operations.facilities.edit` / `operations.facilities.manage` (for an integration key, the admin who minted it). A foreign asset answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Archive an asset",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Asset archived"
          },
          "403": {
            "description": "Refused for the credential (Task H6): `permission_denied` — the member lacks `operations.facilities.delete` / `operations.facilities.manage` (for an integration key, the admin who minted it). A foreign asset answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilities/{facilityId}/children": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ThingId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "List direct child assets",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged child assets",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedThings"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilities/{facilityId}/descendants": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ThingId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "List descendant assets",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged descendant assets",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedThings"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/facilities/{facilityId}/tree": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ThingId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Get nested asset tree",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "depth",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Nested asset tree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AssetTreeNode"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/whatsapp": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Verify webhook subscription",
        "responses": {
          "200": {
            "description": "Returns Meta hub.challenge when verify_token matches"
          },
          "403": {
            "description": "Verification failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Receive WhatsApp messages",
        "responses": {
          "200": {
            "description": "Accepted webhook payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/telegram": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Receive Telegram updates",
        "responses": {
          "200": {
            "description": "Accepted webhook payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp/messages": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Send a WhatsApp message (text + media)",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WhatsAppMessageInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Graph API response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppActionResponse"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6): `platform_number_forbidden` — no `messaging_account_id`, or an account that uses platform credentials (the platform's phone number id, or no token of its own — the seeded \"Laksana Default Number\"); `permission_denied` — the member (for an integration key, its minting admin) lacks `operations.messaging_accounts.manage`. A foreign account answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp/groups": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Create or rename a WhatsApp group",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WhatsAppGroupInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Group creation response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppActionResponse"
                }
              }
            }
          },
          "403": {
            "description": "Platform relay (Task H6 review): WhatsApp groups are created, joined and left with the platform's credentials, so only the master key may call this — any database token gets `platform_key_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp/groups/{groupId}/participants": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Add participants to a WhatsApp group",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GroupId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WhatsAppParticipantsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Participants added/updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppActionResponse"
                }
              }
            }
          },
          "403": {
            "description": "Platform relay (Task H6 review): WhatsApp groups are created, joined and left with the platform's credentials, so only the master key may call this — any database token gets `platform_key_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp/groups/{groupId}/participants/promote": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Promote a WhatsApp group member to admin",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GroupId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WhatsAppPromoteInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Promotion response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppActionResponse"
                }
              }
            }
          },
          "403": {
            "description": "Platform relay (Task H6 review): WhatsApp groups are created, joined and left with the platform's credentials, so only the master key may call this — any database token gets `platform_key_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/inbox": {
      "post": {
        "tags": [
          "Inbox"
        ],
        "summary": "Create or upsert an inbox item",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxItemInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Inbox item created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InboxItem"
                    }
                  }
                }
              }
            }
          },
          "200": {
            "description": "Idempotent match (existing inbox item)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InboxItem"
                    },
                    "idempotent": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_mismatch` — a member token named someone other than itself; `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        },
        "description": "Task H6 re-review 2: a submission from a workspace credential is stored for the Inbox (`status: pending`, `payload.processing.reason = workspace_credential`) and is NOT run through the WhatsApp conversation engine — no auto-classification, no reply to `sender.phone`. Convert it with `POST /v1/inbox/{id}/convert`."
      }
    },
    "/api/v1/inbox/{incomingId}/convert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/IncomingMessageId"
        }
      ],
      "post": {
        "tags": [
          "Inbox"
        ],
        "summary": "Convert an inbox item into a Work Update or Issue",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InboxConversionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Conversion completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/RoutineLog"
                        },
                        {
                          "$ref": "#/components/schemas/Issue"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Inbox item already converted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "conversion": {
                      "type": "object",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_mismatch` — a member token named someone other than itself; `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Login and issue a bearer token",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthLoginInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthLoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Get current user and token info",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authenticated user info",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthMeResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me/work": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "My work across every workspace (Part G1)",
        "description": "The token's person's open work in EVERY workspace they belong to, through the same MyWorkQuery the console's My Work reads — each workspace answers with its own plan, module, role permission and the member's site scope; residents' and inactive memberships never appear. Read-only; tokens stay single-workspace for writes. Only a member's own personal (bearer) token is accepted: an integration api_key and the master key are refused (403 member_required), as is a token whose person has no active membership in the token's (active) workspace. Ungated on every plan.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "section",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "work_orders",
                "issues",
                "inspections",
                "patrols"
              ]
            },
            "description": "Return only this section."
          },
          {
            "name": "per_section",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            },
            "description": "Rows per workspace per section."
          }
        ],
        "responses": {
          "200": {
            "description": "The union, one entry per workspace ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "workspaces": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "name": {
                                "type": "string"
                              },
                              "sections": {
                                "type": "object",
                                "properties": {
                                  "work_orders": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "required": [
                                        "id",
                                        "reference",
                                        "title",
                                        "status",
                                        "site",
                                        "due",
                                        "overdue",
                                        "link"
                                      ],
                                      "properties": {
                                        "id": {
                                          "type": "string",
                                          "format": "uuid"
                                        },
                                        "reference": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Issue / work-order reference; an inspection's stored reference or null; null for patrol routes."
                                        },
                                        "title": {
                                          "type": "string"
                                        },
                                        "status": {
                                          "type": "string",
                                          "description": "work_order_status for work orders, else the record's status."
                                        },
                                        "site": {
                                          "type": [
                                            "object",
                                            "null"
                                          ],
                                          "properties": {
                                            "id": {
                                              "type": "string",
                                              "format": "uuid"
                                            },
                                            "name": {
                                              "type": [
                                                "string",
                                                "null"
                                              ]
                                            }
                                          }
                                        },
                                        "due": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "ISO 8601 in the workspace's timezone: a date-time for work orders and issues, a date (YYYY-MM-DD) for inspections, null for patrol routes."
                                        },
                                        "overdue": {
                                          "type": "boolean",
                                          "description": "Work orders: WorkOrderQuery::isOverdue(). Issues: due before now. Inspections: due date before today in the workspace's timezone."
                                        },
                                        "link": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Public link (/s/issue/{hash}) for issues and work orders; null otherwise."
                                        }
                                      }
                                    }
                                  },
                                  "issues": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "required": [
                                        "id",
                                        "reference",
                                        "title",
                                        "status",
                                        "site",
                                        "due",
                                        "overdue",
                                        "link"
                                      ],
                                      "properties": {
                                        "id": {
                                          "type": "string",
                                          "format": "uuid"
                                        },
                                        "reference": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Issue / work-order reference; an inspection's stored reference or null; null for patrol routes."
                                        },
                                        "title": {
                                          "type": "string"
                                        },
                                        "status": {
                                          "type": "string",
                                          "description": "work_order_status for work orders, else the record's status."
                                        },
                                        "site": {
                                          "type": [
                                            "object",
                                            "null"
                                          ],
                                          "properties": {
                                            "id": {
                                              "type": "string",
                                              "format": "uuid"
                                            },
                                            "name": {
                                              "type": [
                                                "string",
                                                "null"
                                              ]
                                            }
                                          }
                                        },
                                        "due": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "ISO 8601 in the workspace's timezone: a date-time for work orders and issues, a date (YYYY-MM-DD) for inspections, null for patrol routes."
                                        },
                                        "overdue": {
                                          "type": "boolean",
                                          "description": "Work orders: WorkOrderQuery::isOverdue(). Issues: due before now. Inspections: due date before today in the workspace's timezone."
                                        },
                                        "link": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Public link (/s/issue/{hash}) for issues and work orders; null otherwise."
                                        }
                                      }
                                    }
                                  },
                                  "inspections": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "required": [
                                        "id",
                                        "reference",
                                        "title",
                                        "status",
                                        "site",
                                        "due",
                                        "overdue",
                                        "link"
                                      ],
                                      "properties": {
                                        "id": {
                                          "type": "string",
                                          "format": "uuid"
                                        },
                                        "reference": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Issue / work-order reference; an inspection's stored reference or null; null for patrol routes."
                                        },
                                        "title": {
                                          "type": "string"
                                        },
                                        "status": {
                                          "type": "string",
                                          "description": "work_order_status for work orders, else the record's status."
                                        },
                                        "site": {
                                          "type": [
                                            "object",
                                            "null"
                                          ],
                                          "properties": {
                                            "id": {
                                              "type": "string",
                                              "format": "uuid"
                                            },
                                            "name": {
                                              "type": [
                                                "string",
                                                "null"
                                              ]
                                            }
                                          }
                                        },
                                        "due": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "ISO 8601 in the workspace's timezone: a date-time for work orders and issues, a date (YYYY-MM-DD) for inspections, null for patrol routes."
                                        },
                                        "overdue": {
                                          "type": "boolean",
                                          "description": "Work orders: WorkOrderQuery::isOverdue(). Issues: due before now. Inspections: due date before today in the workspace's timezone."
                                        },
                                        "link": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Public link (/s/issue/{hash}) for issues and work orders; null otherwise."
                                        }
                                      }
                                    }
                                  },
                                  "patrols": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "required": [
                                        "id",
                                        "reference",
                                        "title",
                                        "status",
                                        "site",
                                        "due",
                                        "overdue",
                                        "link"
                                      ],
                                      "properties": {
                                        "id": {
                                          "type": "string",
                                          "format": "uuid"
                                        },
                                        "reference": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Issue / work-order reference; an inspection's stored reference or null; null for patrol routes."
                                        },
                                        "title": {
                                          "type": "string"
                                        },
                                        "status": {
                                          "type": "string",
                                          "description": "work_order_status for work orders, else the record's status."
                                        },
                                        "site": {
                                          "type": [
                                            "object",
                                            "null"
                                          ],
                                          "properties": {
                                            "id": {
                                              "type": "string",
                                              "format": "uuid"
                                            },
                                            "name": {
                                              "type": [
                                                "string",
                                                "null"
                                              ]
                                            }
                                          }
                                        },
                                        "due": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "ISO 8601 in the workspace's timezone: a date-time for work orders and issues, a date (YYYY-MM-DD) for inspections, null for patrol routes."
                                        },
                                        "overdue": {
                                          "type": "boolean",
                                          "description": "Work orders: WorkOrderQuery::isOverdue(). Issues: due before now. Inspections: due date before today in the workspace's timezone."
                                        },
                                        "link": {
                                          "type": [
                                            "string",
                                            "null"
                                          ],
                                          "description": "Public link (/s/issue/{hash}) for issues and work orders; null otherwise."
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                              "inbox_review": {
                                "type": "integer",
                                "description": "Inbox messages waiting for review, where the role reads the inbox."
                              }
                            }
                          }
                        },
                        "unread_notifications": {
                          "type": "integer"
                        },
                        "as_of": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "member_required — not a member's own bearer token, or no active membership in the token's active workspace"
          },
          "422": {
            "description": "Invalid section or per_section"
          }
        }
      }
    },
    "/api/v1/groups/{group}/workspaces": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Workspaces of a group, for the mobile switcher (Part G2)",
        "description": "The token's person's workspaces in one organisation group. A member sees the group's attached (not detached) active workspaces in which they hold an active membership (id, name, slug, role_in_group, is_member true, leaving_on). A group admin or viewer (active HQ membership, active entitled group) also sees the other attached workspaces as id, name and is_member false only: no slug, role, date, count, host, owner or feature. A group the person has no link to answers 404, never 403. Only a member's own personal (bearer) token is accepted; an integration api_key and the master key are refused (403 member_required). Read-only; grants nothing inside a workspace.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "group",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The organisation group id."
          }
        ],
        "responses": {
          "200": {
            "description": "The group and its workspaces, ordered by name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "group": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "name": {
                              "type": "string"
                            },
                            "kind": {
                              "type": "string",
                              "enum": [
                                "fm_company",
                                "property_manager",
                                "contractor",
                                "council",
                                "holding"
                              ]
                            }
                          }
                        },
                        "workspaces": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "id",
                              "name",
                              "is_member"
                            ],
                            "properties": {
                              "id": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "name": {
                                "type": "string"
                              },
                              "slug": {
                                "type": "string",
                                "description": "Member rows only."
                              },
                              "role_in_group": {
                                "type": "string",
                                "enum": [
                                  "hq",
                                  "managed",
                                  "client"
                                ],
                                "description": "Member rows only."
                              },
                              "is_member": {
                                "type": "boolean"
                              },
                              "leaving_on": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "format": "date",
                                "description": "Member rows only: the scheduled detach date in the workspace's own timezone (the date the console shows), if any."
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "member_required — not a member's own bearer token, or no active membership in the token's active workspace"
          },
          "404": {
            "description": "not_found — unknown group, or one the person has no link to"
          }
        }
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Revoke current token",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Logged out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthLogoutResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/whatsapp/request-otp": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Request WhatsApp OTP for mobile login",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "example": "+60123456789"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OTP sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests"
          }
        }
      }
    },
    "/api/v1/auth/whatsapp/verify": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Verify WhatsApp OTP and obtain token",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "otp"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "example": "+60123456789"
                  },
                  "otp": {
                    "type": "string",
                    "example": "123456"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthLoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid OTP"
          }
        }
      }
    },
    "/api/v1/user/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Get current user and organisation memberships",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authenticated user info",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthMeResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/messages/classify": {
      "post": {
        "tags": [
          "Centralized Services - Message Classification"
        ],
        "summary": "Classify message using AI",
        "description": "Classify a message to determine if it's a work update, issue, or general inquiry",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "The message text to classify",
                    "example": "Completed AC maintenance at Building A"
                  },
                  "context": {
                    "type": "object",
                    "properties": {
                      "organisation_id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "user_id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "evidence_count": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "source": {
                        "type": "string",
                        "enum": [
                          "whatsapp",
                          "panel",
                          "api"
                        ]
                      },
                      "location_hint": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Classification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClassificationResult"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "429": {
            "description": "Too many AI calls from this workspace (Task H6): one bucket per workspace, `API_AI_HELPER_RATE_PER_MINUTE` (default 30) per minute, shared by the five AI helper routes. The master key is not metered here."
          },
          "500": {
            "description": "Classification failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matching/location": {
      "post": {
        "tags": [
          "Centralized Services - Fuzzy Matching"
        ],
        "summary": "Match location using fuzzy matching",
        "description": "Find matching locations using fuzzy string matching",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "input",
                  "organisation_id"
                ],
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "Location name to match",
                    "example": "Bilding A"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "max_results": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20,
                    "default": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FuzzyMatchResult"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matching/site": {
      "post": {
        "tags": [
          "Centralized Services - Fuzzy Matching"
        ],
        "summary": "Match site using fuzzy matching",
        "description": "Same matcher as `matching/location` (Task H6: both routes reach FuzzyMatchingController::matchSite; `location` answered 500 before).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "input",
                  "organisation_id"
                ],
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "Location name to match",
                    "example": "Bilding A"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "max_results": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20,
                    "default": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FuzzyMatchResult"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matching/things": {
      "post": {
        "tags": [
          "Centralized Services - Fuzzy Matching"
        ],
        "summary": "Match assets/facilities using fuzzy matching",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "input",
                  "organisation_id"
                ],
                "properties": {
                  "input": {
                    "type": "string",
                    "example": "AC Unit"
                  },
                  "location_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FuzzyMatchResult"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matching/category": {
      "post": {
        "tags": [
          "Centralized Services - Fuzzy Matching"
        ],
        "summary": "Match category using fuzzy matching",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "input",
                  "organisation_id"
                ],
                "properties": {
                  "input": {
                    "type": "string",
                    "example": "Maintainance"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "work",
                      "problem"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FuzzyMatchResult"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ai/extract-entities": {
      "post": {
        "tags": [
          "Centralized Services - AI Services"
        ],
        "summary": "Extract entities from message",
        "description": "Extract location, asset, category, and person from message using AI",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message",
                  "organisation_id"
                ],
                "properties": {
                  "message": {
                    "type": "string"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted entities",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractedEntities"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "429": {
            "description": "Too many AI calls from this workspace (Task H6): one bucket per workspace, `API_AI_HELPER_RATE_PER_MINUTE` (default 30) per minute, shared by the five AI helper routes. The master key is not metered here."
          }
        }
      }
    },
    "/api/v1/ai/detect-severity": {
      "post": {
        "tags": [
          "Centralized Services - AI Services"
        ],
        "summary": "Detect issue severity",
        "description": "Detect severity level of an issue using AI",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "work_update",
                      "problem"
                    ],
                    "default": "problem"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Severity detection result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SeverityResult"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H6): `organisation_required` — a database token with no organisation; `organisation_mismatch` — a body/header naming another workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "429": {
            "description": "Too many AI calls from this workspace (Task H6): one bucket per workspace, `API_AI_HELPER_RATE_PER_MINUTE` (default 30) per minute, shared by the five AI helper routes. The master key is not metered here."
          }
        }
      }
    },
    "/api/v1/ai/suggest-category": {
      "post": {
        "tags": [
          "Centralized Services - AI Services"
        ],
        "summary": "Suggest category from message",
        "description": "Suggest the most appropriate category using AI",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message",
                  "organisation_id"
                ],
                "properties": {
                  "message": {
                    "type": "string"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Category suggestion",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategorySuggestion"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "429": {
            "description": "Too many AI calls from this workspace (Task H6): one bucket per workspace, `API_AI_HELPER_RATE_PER_MINUTE` (default 30) per minute, shared by the five AI helper routes. The master key is not metered here."
          }
        }
      }
    },
    "/api/v1/whatsapp-api/acknowledge": {
      "post": {
        "tags": [
          "Centralized Services - WhatsApp"
        ],
        "summary": "Send WhatsApp acknowledgement",
        "description": "Send acknowledgement message using templates",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "incoming_message_id",
                  "template_type",
                  "data"
                ],
                "properties": {
                  "incoming_message_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "template_type": {
                    "type": "string",
                    "enum": [
                      "inbox_received",
                      "work_update_logged",
                      "problem_reported",
                      "work_update_verified",
                      "work_update_rejected",
                      "problem_assigned",
                      "problem_resolved"
                    ]
                  },
                  "data": {
                    "type": "object",
                    "description": "Template-specific data"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledgement sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "result": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `platform_key_required` — only the platform master key may call this relay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp-api/send-guide": {
      "post": {
        "tags": [
          "Centralized Services - WhatsApp"
        ],
        "summary": "Send WhatsApp user guide",
        "description": "Send user guide template",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "guide_type"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "example": "+60123456789"
                  },
                  "guide_type": {
                    "type": "string",
                    "enum": [
                      "general",
                      "work_update",
                      "problem",
                      "structured_format",
                      "quick_format"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Guide sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `platform_key_required` — only the platform master key may call this relay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp-api/send": {
      "post": {
        "tags": [
          "Centralized Services - WhatsApp"
        ],
        "summary": "Send custom WhatsApp message",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "message"
                ],
                "properties": {
                  "phone": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "link": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message sent"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `platform_key_required` — only the platform master key may call this relay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp-api/groups/{group_id}": {
      "delete": {
        "tags": [
          "Centralized Services - WhatsApp"
        ],
        "summary": "Leave a channel's WhatsApp group (platform relay)",
        "description": "Task H6 review. Master key only. The platform number leaves the group, provided a channel of `organisation_id` carries it as `whatsapp_group_id`. laksana-app's channel delete calls this; its failure does not block the delete.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "group_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The Graph API's answer"
          },
          "403": {
            "description": "`platform_key_required` — any database token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "404": {
            "description": "No channel of that organisation carries this group."
          },
          "422": {
            "description": "`organisation_id` missing or not a uuid."
          }
        }
      }
    },
    "/api/v1/telegram-api/send": {
      "post": {
        "tags": [
          "Centralized Services - Telegram"
        ],
        "summary": "Send a free-text Telegram message to a chat id",
        "description": "Community Dedup Phase A (FR-D-040/044). The mirror image of `whatsapp-api/send`, and it exists because `/api/v1/notifications/send` is USER-bound: that endpoint resolves the recipient from a `users` row and reads `telegram_chat_id` off it. An issue subscriber is a resident with no account whose address IS a chat id, so a work order closed in the console or on the tenant portal could reach them on WhatsApp and email but never on Telegram.\n\nQueued through `SendTelegramMessageJob` — never an inline api.telegram.org call from a request worker. Telegram is UNMETERED (telegram blueprint rule 10): no credit is consumed and this endpoint must never grow a billing gate. The send is not written to the delivery log, because a status update is an ack rather than a question and carries no buttons for a tap to resolve against.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chat_id",
                  "message"
                ],
                "properties": {
                  "chat_id": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "A Telegram chat id — digits, negative for a group. NEVER a phone number."
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 4000
                  },
                  "link": {
                    "type": "string",
                    "format": "uri",
                    "description": "Appended to the body on its own line; Telegram's sendMessage takes one text field."
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued for delivery"
          },
          "422": {
            "description": "Validation failed"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `platform_key_required` — only the platform master key may call this relay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/notifications/send": {
      "post": {
        "tags": [
          "Centralized Services - Notifications"
        ],
        "summary": "Send notification",
        "description": "Send multi-channel notification to user",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "user_id",
                  "type",
                  "channels",
                  "data"
                ],
                "properties": {
                  "user_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "work_update_verified",
                      "work_update_rejected",
                      "problem_assigned",
                      "problem_resolved",
                      "problem_status_changed",
                      "reminder"
                    ]
                  },
                  "channels": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "whatsapp",
                        "email",
                        "sms"
                      ]
                    }
                  },
                  "data": {
                    "type": "object",
                    "description": "Notification-specific data"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Notification sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationResult"
                }
              }
            }
          },
          "403": {
            "description": "Platform relay (Task H6 re-review 2): master key only — any database token gets `platform_key_required`. It delivers through the organisation's messaging account, usually the seeded default number (the platform's). Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sites/{siteId}/children": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LocationId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "List direct sub-sites",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged site hierarchy records",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedLocations"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sites/{siteId}/descendants": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LocationId"
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "List all descendant sub-sites",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paged site hierarchy records",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedLocations"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sites/{siteId}/tree": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LocationId"
        },
        {
          "name": "depth",
          "in": "query",
          "schema": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10
          },
          "description": "Maximum hierarchy depth to return."
        }
      ],
      "get": {
        "tags": [
          "Locations & Assets"
        ],
        "summary": "Get a site tree",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Site tree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LocationTree"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ai/assistant/query": {
      "post": {
        "tags": [
          "AI Assistant"
        ],
        "summary": "Ask Budi (Budi Expansion W5): permission-scoped operational data domains + KB-grounded how-to, every answer cited",
        "description": "The organisation is resolved through an ACTIVE membership of the asking user; builders receive the resolved models (cross-org impossible by construction). With the console's trusted service credential, `user_id` asserts the asking member (still membership-checked). Every numeric figure is deterministic; a narration failing the numeric-fidelity guard is replaced by the deterministic rendering. Budi actions W7: a verb + record reference (\"resolve ISS-…\", \"assign ISS-… to <name>\", \"approve MDT-2026-000012\", \"reject MDT-… : reason\") returns state `action_proposal` with an `action` object (token, executor api|console, summary lines, target); nothing is written until `assistant/actions/confirm` is called with that token by the same member within 10 minutes. Booking decisions are `console`-executed (laksana-app), issue actions `api`-executed.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Alternative to the X-Organisation-Id header"
                  },
                  "user_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The asking member, honoured for trusted service credentials only"
                  },
                  "domain": {
                    "type": "string",
                    "description": "Explicit domain key (a disambiguation pick or suggested question); omitted = classifier-first routing",
                    "enum": [
                      "org_health",
                      "issues_trends",
                      "compliance_status",
                      "asset_repairs",
                      "performance",
                      "ratings_summary",
                      "howto",
                      "work_updates",
                      "bookings_pipeline",
                      "bookings_revenue",
                      "stay_occupancy",
                      "collections_receivables",
                      "inventory_stock",
                      "patrol_coverage",
                      "notifications_health"
                    ]
                  },
                  "date_range": {
                    "type": "object",
                    "properties": {
                      "start": {
                        "type": "string",
                        "format": "date"
                      },
                      "end": {
                        "type": "string",
                        "format": "date"
                      },
                      "label": {
                        "type": "string"
                      }
                    }
                  },
                  "site_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "facility_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "category_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "locale": {
                    "type": "string",
                    "maxLength": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Answer or localized state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string",
                      "description": "The localized answer or state text"
                    },
                    "payload": {
                      "type": "object",
                      "nullable": true,
                      "description": "The deterministic domain payload the answer narrated"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "answer",
                        "refusal",
                        "unknown",
                        "disambiguation",
                        "narrow_window",
                        "builder_failed",
                        "not_covered",
                        "disabled"
                      ]
                    },
                    "domain": {
                      "type": "string",
                      "nullable": true
                    },
                    "citations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string",
                            "description": "Localized source label (article title for how-to sources)"
                          },
                          "label_key": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "description": "Builder-authored, router-proven console or /help URL — never LLM-generated"
                          }
                        }
                      }
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Lines the surface must render with the answer (e.g. the fairness framing line)"
                    },
                    "candidates": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Disambiguation options: domain key => localized label"
                    },
                    "available_domains": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "llm",
                            "fallback",
                            "none"
                          ]
                        },
                        "guard": {
                          "type": "string",
                          "enum": [
                            "passed",
                            "violation",
                            "skipped"
                          ]
                        },
                        "window": {
                          "type": "object"
                        },
                        "permission": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an active member of the organisation — or: Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/accounts/{account}/disconnect": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ByonAccountId"
        }
      ],
      "post": {
        "tags": [
          "BYON"
        ],
        "summary": "Disconnect a BYON WhatsApp account",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account disconnected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`permission_denied` (Task H6 re-review): the member (for an integration key, its minting admin) lacks `operations.messaging_accounts.delete` / `.manage`. Another workspace's account answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/accounts/{account}/health": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ByonAccountId"
        }
      ],
      "get": {
        "tags": [
          "BYON"
        ],
        "summary": "Check BYON account health",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account health status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ByonAccountHealth"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/accounts/{account}/webhook/register": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ByonAccountId"
        }
      ],
      "post": {
        "tags": [
          "BYON"
        ],
        "summary": "Register webhook for a BYON account",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "webhook_url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`permission_denied` (Task H6 re-review): lacks `operations.messaging_accounts.edit` / `.manage`. Another workspace's account answers 404.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/accounts/{account}/webhook/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ByonAccountId"
        }
      ],
      "get": {
        "tags": [
          "BYON"
        ],
        "summary": "Verify webhook for a BYON account",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook verification status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verified": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/callback": {
      "post": {
        "tags": [
          "BYON"
        ],
        "summary": "Handle BYON callback from provider",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Callback processed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Task H6: the `state` is not a genuine, unexpired signed state from `byon/signup-url` (HMAC-signed, one hour) — refused for every caller, the master key included. Also: the code exchange or WABA lookup failed."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation. Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.messaging_accounts.create` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Task H6 re-review: `phone_number_id_unavailable` — Meta returned the platform's own phone number id, or one another organisation holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberIdRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/manual-setup": {
      "post": {
        "tags": [
          "BYON"
        ],
        "summary": "Manually set up a BYON account",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone_number_id",
                  "waba_id",
                  "organisation_id"
                ],
                "properties": {
                  "phone_number_id": {
                    "type": "string"
                  },
                  "waba_id": {
                    "type": "string"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "display_name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagingAccount"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation. Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.messaging_accounts.create` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Task H6 review: `phone_number_id_unavailable` — the platform's own phone number id, or one another organisation holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberIdRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byon/signup-url": {
      "post": {
        "tags": [
          "BYON"
        ],
        "summary": "Get BYON signup URL",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signup URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signup_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation. Refused for the credential (Task H6 review): `permission_denied` — the member (for an integration key, the admin who minted it) lacks `operations.messaging_accounts.create` / `.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ComplianceId"
        }
      ],
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get compliance record details",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Compliance record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceRecord"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/{id}/complete": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ComplianceId"
        }
      ],
      "post": {
        "tags": [
          "Compliance"
        ],
        "summary": "Submit compliance completion",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ComplianceCompletionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Completion submitted. Returns the derived record status and any issues raised.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ComplianceCompletionResult"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (unknown status, missing item_id)."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        },
        "description": "Completes an inspection. Number-item statuses and the record status are DERIVED from the submitted readings — a client-supplied status for a number item is ignored (blueprint 20260817 §0 rule 3). Out-of-range readings raise or update a preventive work order when the template's `auto_issue_on_breach` is on and the workspace holds `cmms_work_orders`."
      }
    },
    "/api/v1/compliance/{id}/history": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ComplianceId"
        }
      ],
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get compliance record history",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "History entries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceHistoryEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/{id}/reject": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ComplianceId"
        }
      ],
      "post": {
        "tags": [
          "Compliance"
        ],
        "summary": "Reject a compliance record",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Record rejected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceRecord"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/{id}/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ComplianceId"
        }
      ],
      "post": {
        "tags": [
          "Compliance"
        ],
        "summary": "Verify a compliance record",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Record verified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceRecord"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/pending": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "List pending compliance records",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged pending compliance records",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedComplianceRecords"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/records/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ComplianceRecordId"
        }
      ],
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get compliance record details by record ID",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Record details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceRecord"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/summary": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get compliance summary",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Compliance summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceSummary"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/verification/pending": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "List pending verifications",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pending verifications",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceRecord"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/compliance/verification/stats": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get verification statistics",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verification stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pending": {
                      "type": "integer"
                    },
                    "verified": {
                      "type": "integer"
                    },
                    "rejected": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `not_a_member` — the token's member is no longer an active member of the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/issues": {
      "get": {
        "tags": [
          "Issues"
        ],
        "summary": "List issues",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged issues",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Issue"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/issues/{issueId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/IssueId"
        }
      ],
      "get": {
        "tags": [
          "Issues"
        ],
        "summary": "Get an issue by ID",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Issue record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Issue"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/knowledge-base/search": {
      "get": {
        "tags": [
          "Knowledge Base"
        ],
        "summary": "Search the knowledge base",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organisation_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/KnowledgeBaseResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/knowledge-base/suggest": {
      "post": {
        "tags": [
          "Knowledge Base"
        ],
        "summary": "Suggest a knowledge base entry",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "question",
                  "answer"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "question": {
                    "type": "string"
                  },
                  "answer": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Suggestion created"
          },
          "403": {
            "description": "Refused for the credential (Task H6): `organisation_required` — a database token with no organisation; `organisation_mismatch` — a body/header naming another workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "429": {
            "description": "Too many AI calls from this workspace (Task H6): one bucket per workspace, `API_AI_HELPER_RATE_PER_MINUTE` (default 30) per minute, shared by the five AI helper routes. The master key is not metered here."
          }
        }
      }
    },
    "/api/v1/mobile/chat/messages": {
      "get": {
        "tags": [
          "Mobile"
        ],
        "summary": "List mobile chat messages",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MobileChatMessage"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Mobile"
        ],
        "summary": "Send a mobile chat message",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "message"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Message sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MobileChatMessage"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/mobile/dashboard": {
      "get": {
        "tags": [
          "Mobile"
        ],
        "summary": "Get mobile dashboard data",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dashboard data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MobileDashboardResponse"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/mobile/problems": {
      "get": {
        "tags": [
          "Mobile"
        ],
        "summary": "List mobile problems",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Problems list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MobileProblem"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Mobile"
        ],
        "summary": "Report a problem from mobile",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "description"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "description": {
                    "type": "string"
                  },
                  "site_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "facility_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "category_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "severity": {
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high",
                      "critical"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Problem reported",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Issue"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/mobile/updates": {
      "get": {
        "tags": [
          "Mobile"
        ],
        "summary": "List mobile work updates",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updates list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MobileUpdate"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Mobile"
        ],
        "summary": "Create a work update from mobile",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "remarks"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "remarks": {
                    "type": "string"
                  },
                  "site_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "facility_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "category_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Work update created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/RoutineLog"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/notification-preferences/": {
      "get": {
        "tags": [
          "Notification Preferences"
        ],
        "summary": "Get notification preferences",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Notification preferences",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationPreference"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Notification Preferences"
        ],
        "summary": "Update notification preferences",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationPreferenceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preferences updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationPreference"
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/notifications/device-token": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Register a device token for push notifications",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "platform"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "platform": {
                    "type": "string",
                    "enum": [
                      "ios",
                      "android"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Device token registered"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Notifications"
        ],
        "summary": "Remove a device token",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device token removed"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/reporting/leaderboard": {
      "get": {
        "tags": [
          "Reporting"
        ],
        "summary": "Get performance leaderboard",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Leaderboard data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LeaderboardEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/whatsapp/groups/{groupId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/GroupId"
        }
      ],
      "delete": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Delete a WhatsApp group",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Group deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppActionResponse"
                }
              }
            }
          },
          "403": {
            "description": "Platform relay (Task H6 review): WhatsApp groups are created, joined and left with the platform's credentials, so only the master key may call this — any database token gets `platform_key_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/webhooks/whatsapp/simulate": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Simulate a WhatsApp webhook event",
        "description": "QA/sandbox injector. Requires an API token: the organisation is taken from that token, never from the body. `organisation_id` may still be supplied for backwards compatibility but must match the token's organisation — a mismatch is a 403. Secured 2026-08-13; before that this route was unauthenticated and could write an inbound message into any organisation whose UUID the caller knew.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Simulation processed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "401": {
            "description": "No or invalid API token"
          },
          "403": {
            "description": "organisation_id does not match the token's organisation"
          }
        }
      }
    },
    "/webhooks/telegram/{messagingAccount}": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Telegram webhook — branded per-tenant bot",
        "description": "Inbound Telegram updates for one organisation's own (BYOB) bot. The path names the messaging account; the request authenticates with that account's own X-Telegram-Bot-Api-Secret-Token (hash_equals, fail-closed). Same processing as the platform route, on the branded bot's identity.",
        "parameters": [
          {
            "name": "messagingAccount",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-Telegram-Bot-Api-Secret-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A Telegram Bot API Update object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Update accepted (processing is queued)"
          },
          "403": {
            "description": "Unknown account or wrong per-bot secret"
          }
        }
      }
    },
    "/v1/telegram/bot/validate": {
      "post": {
        "tags": [
          "Telegram BYOB"
        ],
        "summary": "Validate a BotFather token (getMe), storing nothing",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "bot_token"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "bot_token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token valid — bot id/username/name for visual confirmation"
          },
          "422": {
            "description": "Telegram rejected the token"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/v1/telegram/bot/connect": {
      "post": {
        "tags": [
          "Telegram BYOB"
        ],
        "summary": "Connect a branded bot: validate, store encrypted, register the per-bot webhook",
        "description": "Plan-gated on the telegram_channel feature. The token and a generated per-bot webhook secret are stored encrypted on the organisation's telegram messaging account; setWebhook targets /webhooks/telegram/{account}.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "bot_token"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "bot_token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Connected"
          },
          "403": {
            "description": "telegram_channel feature not on the plan — or: Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_mismatch` — a member token named someone other than itself. `permission_denied` (Task H6 re-review): the member (for an integration key, its minting admin) lacks `administration.organisation.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Invalid token"
          },
          "502": {
            "description": "Token valid but webhook registration failed — account left in the attention state"
          }
        }
      }
    },
    "/v1/telegram/bot/health": {
      "get": {
        "tags": [
          "Telegram BYOB"
        ],
        "summary": "Branded-bot health: state, getWebhookInfo freshness, last error",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "state = not_connected | healthy | attention (incl. token_revoked)"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/v1/telegram/bot/test": {
      "post": {
        "tags": [
          "Telegram BYOB"
        ],
        "summary": "Queue a test message to the requesting user's own Telegram DM",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "user_id"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "user_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued"
          },
          "422": {
            "description": "The user has not connected Telegram (no_telegram_identity)"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/v1/telegram/bot": {
      "delete": {
        "tags": [
          "Telegram BYOB"
        ],
        "summary": "Disconnect the branded bot (deleteWebhook + disable), falling back to the platform bot",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Disconnected; notifications fall back to the platform bot"
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_mismatch` — a member token named someone other than itself. `permission_denied` (Task H6 re-review): the member (for an integration key, its minting admin) lacks `administration.organisation.manage`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/health/telegram": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Telegram channel health (parity with /health/whatsapp)",
        "description": "Database + configuration + live getWebhookInfo freshness + last-hour delivery counters. 503 when unhealthy.",
        "responses": {
          "200": {
            "description": "healthy or degraded"
          },
          "503": {
            "description": "unhealthy — configuration missing or webhook unregistered"
          }
        }
      }
    },
    "/api/v1/ai/config-agent/propose": {
      "post": {
        "tags": [
          "AI Assistant"
        ],
        "summary": "Configuration Agent (W10): turn an instruction into a PROPOSAL of allowlisted setting changes — never applied here",
        "description": "The one Configuration Agent step that runs in laksana-api, because the LLM provider key lives here. Resolution is the Budi rule: the organisation comes from an ACTIVE membership of the asking member (cross-org impossible); the console's trusted service credential may assert `user_id`. The workspace must carry the `config_agent` feature (403 `feature_missing` otherwise). The reply is inert data: at most 5 changes, each `{target_type, target_id, target_label, path, to}`, every one on the mirrored `ConfigAgentAllowlist` (checklist item rules, `auto_issue_on_breach`, the compliance escalation ladder) and on a target the model was shown. Refusals are reasons on a 200 (`assistant_disabled`, `llm_disabled`, `consent_off`, `budget_exhausted`, `provider_failed`, `nothing_to_configure`, `nothing_proposed`). Confirm, apply, reject and rollback are console-side (laksana-app) and never reach this api. Proposals are counted on attempt against `max_config_proposals_per_day` (add-on override → plan → 20; 0 = unlimited). W11 (2026-09-15): changes are `{op: set|create, …}` over eight targets — compliance_template, work_update_checklist (incl. timing/switches), notification_rules and evidence_policy (singletons, target_id \"organisation\"), notification_event (target_id = catalogue type), and create-only site / category / facility with `fields`. Up to 10 changes. The api still only proposes; laksana-app applies.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "instruction"
                ],
                "properties": {
                  "instruction": {
                    "type": "string",
                    "maxLength": 1000
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Alternative to the X-Organisation-Id header"
                  },
                  "user_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The asking member, honoured for trusted service credentials only"
                  },
                  "locale": {
                    "type": "string",
                    "maxLength": 5,
                    "description": "Hint for the rationale's language"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A proposal, or a reason",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "assistant_disabled",
                        "llm_disabled",
                        "unknown_organisation",
                        "consent_off",
                        "budget_exhausted",
                        "provider_failed",
                        "nothing_to_configure",
                        "nothing_proposed"
                      ]
                    },
                    "rationale": {
                      "type": "string",
                      "nullable": true
                    },
                    "changes": {
                      "type": "array",
                      "maxItems": 5,
                      "items": {
                        "type": "object",
                        "properties": {
                          "target_type": {
                            "type": "string",
                            "enum": [
                              "compliance_template",
                              "work_update_checklist"
                            ]
                          },
                          "target_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "target_label": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string",
                            "description": "auto_issue_on_breach | items.{item_id}.{type|unit|min_value|max_value|severity|require_evidence_on_breach} | notification_settings.{reminder_days_before|reminder_on_due_date|escalation_days_overdue|notify_roles|notification_channels}"
                          },
                          "to": {}
                        }
                      }
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "enum": [
                              "not_json",
                              "unknown_target",
                              "unknown_item",
                              "path_not_allowed",
                              "invalid_value",
                              "too_many_changes",
                              "duplicate_path"
                            ]
                          },
                          "path": {
                            "type": "string",
                            "nullable": true
                          },
                          "detail": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "model": {
                      "type": "string",
                      "nullable": true
                    },
                    "budget": {
                      "type": "object",
                      "properties": {
                        "limit": {
                          "type": "integer",
                          "nullable": true,
                          "description": "null = unlimited"
                        },
                        "used": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Organisation context missing"
          },
          "401": {
            "description": "Unauthenticated"
          },
          "403": {
            "description": "Not an active member, or the workspace lacks the config_agent feature (`reason: feature_missing`) — or: Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/api/v1/ai/assistant/context": {
      "get": {
        "tags": [
          "AI Assistant"
        ],
        "summary": "Budi's opening context: whether it is enabled and the asker's permission-filtered capability surface with suggested questions",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "user_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Capability surface",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "available_domains": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "suggested": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "message": {
                      "type": "string",
                      "description": "Localized disabled-state line when enabled is false"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an active member of the organisation — or: Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/inventory-items": {
      "get": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "listInventoryItems",
        "summary": "List inventory items",
        "description": "Tenant-scoped by the token. Requires operations.inventory.view or .manage. `low_stock` uses THE one low-stock definition (current_stock <= minimum_stock, zero included).",
        "parameters": [
          {
            "name": "site_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "low_stock",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring over name / sku / part_number."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated items (25/page)."
          },
          "403": {
            "description": "No inventory permission or feature not entitled."
          }
        }
      }
    },
    "/api/v1/inventory-items/lookup": {
      "get": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "lookupInventoryItemByBarcode",
        "summary": "Exact barcode lookup",
        "description": "Exact `=` match on the indexed (organisation, barcode) pair — never a substring search. Barcodes are not unique (real warehouses share supplier barcodes across pack sizes), so `data` may hold several items; empty is the honest no-match answer.",
        "parameters": [
          {
            "name": "barcode",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching items (possibly several, possibly none).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InventoryItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "No inventory permission or feature not entitled."
          }
        }
      }
    },
    "/api/v1/inventory-items/{inventoryItem}": {
      "get": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "showInventoryItem",
        "summary": "One item with its recent ledger",
        "parameters": [
          {
            "name": "inventoryItem",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The item plus its latest 20 transactions."
          },
          "404": {
            "description": "Not found in the token organisation."
          }
        }
      }
    },
    "/api/v1/inventory-items/{inventoryItem}/receive": {
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "receiveInventoryStock",
        "summary": "Receive stock (TYPE_RECEIPT)",
        "parameters": [
          {
            "name": "inventoryItem",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quantity"
                ],
                "properties": {
                  "quantity": {
                    "type": "number",
                    "exclusiveMinimum": 0
                  },
                  "reference": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stock movement written through InventoryService (the one stock writer).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "transaction_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "stock_after": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Member lacks operations.inventory.manage, or token scope mismatch. — or: Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "404": {
            "description": "Item not found in the token organisation (existence never leaks across tenants)."
          },
          "422": {
            "description": "Refused by the service (insufficient stock, zero quantity)."
          }
        }
      }
    },
    "/api/v1/inventory-items/{inventoryItem}/issue": {
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "issueInventoryStock",
        "summary": "Issue stock (TYPE_ISSUE)",
        "description": "Negative resulting stock is refused with a 422 — the ledger never goes below zero.",
        "parameters": [
          {
            "name": "inventoryItem",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quantity"
                ],
                "properties": {
                  "quantity": {
                    "type": "number",
                    "exclusiveMinimum": 0
                  },
                  "reference": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stock movement written through InventoryService (the one stock writer).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "transaction_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "stock_after": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Member lacks operations.inventory.manage, or token scope mismatch. — or: Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "404": {
            "description": "Item not found in the token organisation (existence never leaks across tenants)."
          },
          "422": {
            "description": "Refused by the service (insufficient stock, zero quantity)."
          }
        }
      }
    },
    "/api/v1/inventory-items/{inventoryItem}/adjust": {
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "adjustInventoryStock",
        "summary": "Set an absolute stock level (TYPE_ADJUSTMENT)",
        "description": "The manual correction verb. Stock-take completions do NOT use this — they apply count-time variances as relative deltas server-side.",
        "parameters": [
          {
            "name": "inventoryItem",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "new_stock"
                ],
                "properties": {
                  "new_stock": {
                    "type": "number",
                    "minimum": 0
                  },
                  "reference": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stock movement written through InventoryService (the one stock writer).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "transaction_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "stock_after": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Member lacks operations.inventory.manage, or token scope mismatch. — or: Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "404": {
            "description": "Item not found in the token organisation (existence never leaks across tenants)."
          },
          "422": {
            "description": "Refused by the service (insufficient stock, zero quantity)."
          }
        }
      }
    },
    "/api/v1/stock-takes": {
      "get": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "listStockTakes",
        "summary": "List stock-take sessions",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "counting",
                "review",
                "completed",
                "cancelled"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sessions (25/page)."
          }
        }
      },
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "createStockTake",
        "summary": "Create AND start a counting session",
        "description": "Freezes the line set from the scoped ACTIVE catalogue and starts counting immediately (an API client is a person in the store room; the console keeps a separate draft step for supervisors). Requires operations.inventory.manage.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "site_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true
                  },
                  "category": {
                    "type": "string",
                    "maxLength": 50,
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The counting session with its lines.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InventoryStockTake"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Refused — e.g. empty_scope (no active items match). `reason` carries the machine constant; `message` the localized line."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/stock-takes/{stockTake}": {
      "get": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "showStockTake",
        "summary": "One session with its lines",
        "parameters": [
          {
            "name": "stockTake",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The session."
          },
          "404": {
            "description": "Not found in the token organisation."
          }
        }
      }
    },
    "/api/v1/stock-takes/{stockTake}/count": {
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "recordStockTakeCount",
        "summary": "Record one line's count",
        "description": "expected_quantity snapshots the system stock AT THIS MOMENT (never at session start) and variance = counted - expected. A recount re-snapshots. Negative counts are refused.",
        "parameters": [
          {
            "name": "stockTake",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "line_id",
                  "quantity"
                ],
                "properties": {
                  "line_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "quantity": {
                    "type": "number",
                    "minimum": 0
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The counted line.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InventoryStockTakeLine"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Session or line not found in the token organisation."
          },
          "422": {
            "description": "Refused (terminal session, wrong status, negative count) — `reason` carries the constant."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/stock-takes/{stockTake}/complete": {
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "completeStockTake",
        "summary": "Apply the count",
        "description": "Each non-zero variance is applied as a RELATIVE delta through the one stock writer (a movement between count and completion survives), stamped per line so a retry never double-adjusts. Zero-variance lines write nothing. Uncounted lines require acknowledge_uncounted=true and become skipped — stock untouched, on record.",
        "parameters": [
          {
            "name": "stockTake",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "acknowledge_uncounted": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What was written.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "adjusted": {
                          "type": "integer"
                        },
                        "skipped": {
                          "type": "integer"
                        },
                        "unchanged": {
                          "type": "integer"
                        },
                        "session": {
                          "$ref": "#/components/schemas/InventoryStockTake"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Refused — uncounted_lines without the acknowledgement, or a terminal session."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/stock-takes/{stockTake}/cancel": {
      "post": {
        "tags": [
          "CMMS Inventory"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "cancelStockTake",
        "summary": "Cancel a session (applies nothing)",
        "parameters": [
          {
            "name": "stockTake",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "note"
                ],
                "properties": {
                  "note": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The cancelled session — kept as the audit trail of an abandoned count, never deleted."
          },
          "422": {
            "description": "Already terminal."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/dedup/ai-score": {
      "post": {
        "tags": [
          "Issues"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "aiDedupScore",
        "summary": "AI dedup scoring relay (server-to-server)",
        "description": "W8: the HTTP half of AiScoreRelay for the repos that hold no AI provider. AiDedupScoringGate enforces the whole platform/consent/budget ladder here, beside the provider key. A POLICY refusal (llm_disabled, consent_off, budget_exhausted, unknown_organisation) is a 200 with ran:false + reason — never a 4xx — so the relay can tell policy from transport. Not intended for end-user clients.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "organisation_id",
                  "system",
                  "prompt"
                ],
                "properties": {
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "system": {
                    "type": "string",
                    "maxLength": 4000
                  },
                  "prompt": {
                    "type": "string",
                    "maxLength": 60000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The gate outcome.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "ran": {
                          "type": "boolean"
                        },
                        "reason": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "llm_disabled",
                            "consent_off",
                            "budget_exhausted",
                            "unknown_organisation",
                            "provider_failed",
                            null
                          ]
                        },
                        "text": {
                          "type": "string",
                          "nullable": true,
                          "description": "The raw model response the mirrored scoring service parses."
                        },
                        "model": {
                          "type": "string",
                          "nullable": true,
                          "example": "gemini/gemini-2.5-flash"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid service credential."
          },
          "422": {
            "description": "Malformed payload."
          },
          "403": {
            "description": "Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ai/assistant/actions/confirm": {
      "post": {
        "tags": [
          "AI Assistant"
        ],
        "summary": "Confirm a pending Budi action (W7) — executes issue actions; a booking decision answers action_console_only",
        "description": "The token binds the proposal to the organisation and member who asked; a confirm from anyone else, or after the 10-minute TTL, is `action_expired`. Permission is re-checked at confirm time. A rejection that still needs its reason accepts it here (`reason`).",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Required when the proposal has needs_reason (booking rejection)"
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "user_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The confirming member, honoured for trusted service credentials only"
                  },
                  "locale": {
                    "type": "string",
                    "maxLength": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pipeline-shaped result: state (action_done | action_failed | action_expired | action_refused | action_needs_reason | action_console_only | action_cancelled), answer text, citations (the record), meta."
          },
          "403": {
            "description": "Not an active member of the organisation. — or: Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          },
          "422": {
            "description": "Validation error."
          }
        }
      }
    },
    "/api/v1/ai/assistant/actions/cancel": {
      "post": {
        "tags": [
          "AI Assistant"
        ],
        "summary": "Cancel a pending Budi action (W7)",
        "description": "Forgets the proposal token for the asking member. Always answers action_cancelled.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "organisation_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "user_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "locale": {
                    "type": "string",
                    "maxLength": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "state action_cancelled."
          },
          "403": {
            "description": "Not an active member of the organisation. — or: Refused for the credential (Task H5): `organisation_mismatch` — a database token named a workspace other than its own (body, query, nested copy or X-Organisation-Id); `organisation_required` — a database token with no organisation; `member_required` — this endpoint needs a member behind the request; an integration key has none (or none on a write); `member_mismatch` — a member token named someone other than itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CredentialRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/meter-readings": {
      "get": {
        "tags": [
          "CMMS"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "listMeterReadings",
        "summary": "List meter readings",
        "description": "Tenant-scoped by the token. Requires operations.facilities.view or .manage.",
        "parameters": [
          {
            "name": "facility_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "meter_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "runtime_hours",
                "cycles",
                "mileage",
                "energy",
                "custom"
              ]
            }
          },
          {
            "name": "meter_label",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated readings (25/page), newest first."
          },
          "403": {
            "description": "No facilities permission or cmms_meter_readings not entitled."
          }
        }
      },
      "post": {
        "tags": [
          "CMMS"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "operationId": "createMeterReading",
        "summary": "Record a meter reading",
        "description": "Gap review G-07 (2026-09-16). Requires operations.facilities.view or .manage. Cumulative meter types (runtime_hours, cycles, mileage, energy) reject a value lower than the previous reading in the same series unless is_meter_reset is true. Creating a reading evaluates the facility's active meter trigger rules and may raise a preventive-maintenance work order.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "facility_id",
                  "meter_type",
                  "reading_value"
                ],
                "properties": {
                  "facility_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "meter_type": {
                    "type": "string",
                    "enum": [
                      "runtime_hours",
                      "cycles",
                      "mileage",
                      "energy",
                      "custom"
                    ]
                  },
                  "meter_label": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Distinguishes multiple series of the same type on one asset."
                  },
                  "reading_value": {
                    "type": "number"
                  },
                  "unit": {
                    "type": "string",
                    "maxLength": 50,
                    "nullable": true,
                    "description": "Defaults per meter_type when omitted (h / cycles / km / kWh); required for custom."
                  },
                  "reading_date": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "Defaults to now."
                  },
                  "is_meter_reset": {
                    "type": "boolean",
                    "nullable": true,
                    "description": "Set when the physical meter was replaced or rolled over, to allow a lower value in a cumulative series."
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored reading."
          },
          "403": {
            "description": "No facilities permission or cmms_meter_readings not entitled."
          },
          "422": {
            "description": "facility_id not found in this workspace, a negative value, a future reading_date, or a cumulative reading lower than the previous one in its series."
          }
        }
      }
    },
    "/api/v1/work-orders": {
      "get": {
        "tags": [
          "Work Orders"
        ],
        "summary": "List my work orders (Part W)",
        "description": "The work orders the token's own member may see, through the same WorkOrderQuery scope, tabs and filters as the console (site/facility/category scope; a vendor sees only their own jobs). Ordered planned_start_at ascending (unscheduled last), then newest first. 403 member_required for the master key and for workspace integration keys (reads included); 403 organisation_required for a token bound to no workspace; 403 when the workspace lacks the cmms_work_orders plan feature (feature wall) or the maintenance module, or the member is inactive or holds no operations.work_orders.* key.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "tab",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "requested",
                "awaiting_approval",
                "approved",
                "scheduled",
                "in_progress",
                "on_hold",
                "completed",
                "closed",
                "assigned_to_me",
                "my_team",
                "vendors"
              ],
              "default": "open"
            },
            "description": "The console's tab."
          },
          {
            "name": "statuses[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "description": "Work-order statuses."
          },
          {
            "name": "site_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": ""
          },
          {
            "name": "facility_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": ""
          },
          {
            "name": "work_order_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 40
            },
            "description": ""
          },
          {
            "name": "assignee_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": ""
          },
          {
            "name": "team_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": ""
          },
          {
            "name": "vendor_team_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "An external (vendor) team."
          },
          {
            "name": "scheduled_from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "planned_start_at >= ISO-8601; no offset = wall time in the workspace's timezone."
          },
          {
            "name": "scheduled_to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "planned_start_at < ISO-8601; no offset = wall time in the workspace's timezone."
          },
          {
            "name": "overdue",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1",
                "true",
                "false"
              ]
            },
            "description": "Open, not completed, and COALESCE(planned_end_at, sla_due_at) in the past."
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            },
            "description": ""
          }
        ],
        "responses": {
          "200": {
            "description": "A page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkOrder"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused (see description)"
          },
          "422": {
            "description": "Validation error"
          }
        }
      }
    },
    "/api/v1/work-orders/{workOrder}": {
      "get": {
        "tags": [
          "Work Orders"
        ],
        "summary": "Read one work order (Part W)",
        "description": "404 when the work order is not in the member's scope — including another workspace's — never 403. 403 member_required for the master key and for workspace integration keys (reads included); 403 organisation_required for a token bound to no workspace; 403 when the workspace lacks the cmms_work_orders plan feature (feature wall) or the maintenance module, or the member is inactive or holds no operations.work_orders.* key.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The work order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WorkOrder"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused"
          },
          "404": {
            "description": "Not in scope"
          }
        }
      }
    },
    "/api/v1/work-orders/{workOrder}/transition": {
      "post": {
        "tags": [
          "Work Orders"
        ],
        "summary": "Move a work order (Part W)",
        "description": "Moves the work order through the lifecycle service with the console's rules (WorkOrderAbilities): approve/reject need operations.work_orders.approve; scheduling, cancelling and closing need operations.work_orders.manage; in_progress/on_hold/completed need .manage or being the member doing the job; a vendor never approves, schedules or cancels. 'scheduled' needs a window (planned_start_at and planned_end_at, ISO-8601; with an offset or Z that instant, WITHOUT one wall time in the workspace's timezone; stored and returned in UTC). Closing or cancelling a job reopens the Bookings dates it closed, exactly as the console does. v1 takes no photos: completing a job in a workspace with the resolution-photo mandate is a 422 (evidence) — finish it in the console. Scheduling a facility whose booking profile closes bookings during work orders is a 422 (planned_start_at) — schedule it in the console so the bookings are closed too. 404 outside scope. 403 member_required for the master key and for workspace integration keys (reads included); 403 organisation_required for a token bound to no workspace; 403 when the workspace lacks the cmms_work_orders plan feature (feature wall) or the maintenance module, or the member is inactive or holds no operations.work_orders.* key.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workOrder",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "enum": [
                      "requested",
                      "pending_approval",
                      "approved",
                      "scheduled",
                      "in_progress",
                      "on_hold",
                      "completed",
                      "closed",
                      "rejected",
                      "cancelled"
                    ]
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 2000
                  },
                  "planned_start_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "ISO-8601. No offset = wall time in the workspace's timezone."
                  },
                  "planned_end_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "ISO-8601. No offset = wall time in the workspace's timezone."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WorkOrder"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not permitted for this member, or refused caller"
          },
          "404": {
            "description": "Not in scope"
          },
          "422": {
            "description": "Invalid transition, missing window, photo mandate, booking closure (console only), or validation"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    },
    "parameters": {
      "OrganisationId": {
        "name": "organisationId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "MessagingAccountId": {
        "name": "messagingAccountId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ChannelId": {
        "name": "channelId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "LocationId": {
        "name": "siteId",
        "in": "path",
        "required": true,
        "description": "Location ID",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ThingId": {
        "name": "facilityId",
        "in": "path",
        "required": true,
        "description": "Asset ID",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "GroupId": {
        "name": "groupId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "IncomingMessageId": {
        "name": "incomingId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ByonAccountId": {
        "name": "account",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "BYON account ID"
      },
      "ComplianceId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Compliance record ID"
      },
      "ComplianceRecordId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Compliance record (records) ID"
      },
      "IssueId": {
        "name": "issueId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "schemas": {
      "PaginatedOrganisations": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Organisation"
            }
          },
          "links": {
            "type": "object",
            "properties": {
              "first": {
                "type": "string",
                "format": "uri"
              },
              "next": {
                "type": "string",
                "format": "uri"
              },
              "prev": {
                "type": "string",
                "format": "uri"
              },
              "last": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "current_page": {
                "type": "integer"
              },
              "last_page": {
                "type": "integer"
              },
              "per_page": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              }
            }
          }
        }
      },
      "PaginatedMessagingAccounts": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessagingAccount"
            }
          },
          "links": {
            "$ref": "#/components/schemas/PaginationLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "PaginatedChannels": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            }
          },
          "links": {
            "$ref": "#/components/schemas/PaginationLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "PaginatedLocations": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Location"
            }
          },
          "links": {
            "$ref": "#/components/schemas/PaginationLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "PaginatedThings": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Asset"
            }
          },
          "links": {
            "$ref": "#/components/schemas/PaginationLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "PaginationLinks": {
        "type": "object",
        "properties": {
          "first": {
            "type": "string",
            "format": "uri"
          },
          "next": {
            "type": "string",
            "format": "uri"
          },
          "prev": {
            "type": "string",
            "format": "uri"
          },
          "last": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "current_page": {
            "type": "integer"
          },
          "last_page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "Organisation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "suspended"
            ]
          },
          "operating_hours": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "settings": {
            "type": "object"
          },
          "default_messaging_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OrganisationInput": {
        "type": "object",
        "required": [
          "name",
          "slug"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "settings": {
            "type": "object"
          },
          "default_messaging_account_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "MessagingAccount": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string",
            "enum": [
              "whatsapp",
              "telegram"
            ]
          },
          "provider": {
            "type": "string"
          },
          "waba_id": {
            "type": "string"
          },
          "phone_number_id": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "is_default": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "degraded",
              "disabled"
            ]
          },
          "settings": {
            "type": "object"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MessagingAccountInput": {
        "type": "object",
        "required": [
          "organisation_id",
          "platform",
          "display_name"
        ],
        "properties": {
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string",
            "enum": [
              "whatsapp",
              "telegram"
            ]
          },
          "provider": {
            "type": "string"
          },
          "waba_id": {
            "type": "string"
          },
          "phone_number_id": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "is_default": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "degraded",
              "disabled"
            ]
          },
          "settings": {
            "type": "object"
          }
        }
      },
      "Channel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "messaging_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "group",
              "direct"
            ]
          },
          "purpose": {
            "type": "string",
            "enum": [
              "ops",
              "external",
              "read_only"
            ]
          },
          "name": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "allow_routine_logs": {
            "type": "boolean",
            "description": "Whether this channel is allowed to create work updates (work_update intent)."
          },
          "allow_issues": {
            "type": "boolean",
            "description": "Whether this channel is allowed to create problems (problem intent)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "last_webhook_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChannelInput": {
        "type": "object",
        "required": [
          "organisation_id",
          "name"
        ],
        "properties": {
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "messaging_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string",
            "enum": [
              "whatsapp",
              "telegram"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "group",
              "direct"
            ]
          },
          "purpose": {
            "type": "string",
            "enum": [
              "ops",
              "external",
              "read_only"
            ]
          },
          "name": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "allow_routine_logs": {
            "type": "boolean",
            "description": "Whether this channel is allowed to create work updates (work_update intent)."
          },
          "allow_issues": {
            "type": "boolean",
            "description": "Whether this channel is allowed to create problems (problem intent)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          }
        }
      },
      "Location": {
        "type": "object",
        "title": "Location",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "operating_hours": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "supervisor_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "type": "string",
            "example": "site"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "path": {
            "type": "string",
            "example": "KL Tower / Level 1"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "latitude": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "longitude": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          }
        }
      },
      "LocationInput": {
        "type": "object",
        "title": "Location input",
        "required": [
          "organisation_id",
          "name"
        ],
        "properties": {
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "operating_hours": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "supervisor_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "external_reference_no": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "example": "area"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "latitude": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "longitude": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          }
        }
      },
      "Asset": {
        "type": "object",
        "title": "Asset",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "site_id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "parent_facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "reference": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "asset",
              "building",
              "floor",
              "zone",
              "station",
              "equipment",
              "locker",
              "toilet",
              "unit",
              "space",
              "facility",
              "needs_service",
              "other"
            ]
          },
          "path": {
            "type": "string",
            "example": "Building A / Floor 1 / Unit 1-1"
          },
          "external_reference_no": {
            "type": "string",
            "nullable": true
          },
          "asset_tag": {
            "type": "string",
            "nullable": true
          },
          "area_zone": {
            "type": "string",
            "nullable": true
          },
          "latitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "longitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "address_line_1": {
            "type": "string",
            "nullable": true
          },
          "address_line_2": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string",
            "nullable": true
          },
          "postal_code": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true,
            "example": "MY"
          },
          "formatted_address": {
            "type": "string",
            "nullable": true
          },
          "place_id": {
            "type": "string",
            "nullable": true
          },
          "address_summary": {
            "type": "string",
            "nullable": true
          },
          "address_inherited": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "metadata": {
            "type": "object"
          },
          "children_count": {
            "type": "integer",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ThingInput": {
        "type": "object",
        "title": "Asset input",
        "required": [
          "organisation_id",
          "site_id",
          "name"
        ],
        "properties": {
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "site_id": {
            "type": "string",
            "format": "uuid"
          },
          "parent_facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "external_reference_no": {
            "type": "string",
            "nullable": true
          },
          "asset_tag": {
            "type": "string",
            "nullable": true
          },
          "area_zone": {
            "type": "string",
            "nullable": true
          },
          "latitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "longitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "address_line_1": {
            "type": "string",
            "nullable": true
          },
          "address_line_2": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string",
            "nullable": true
          },
          "postal_code": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true,
            "example": "MY"
          },
          "formatted_address": {
            "type": "string",
            "nullable": true
          },
          "place_id": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "metadata": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "asset",
                  "building",
                  "floor",
                  "zone",
                  "station",
                  "equipment",
                  "locker",
                  "toilet",
                  "unit",
                  "space",
                  "facility",
                  "needs_service",
                  "other"
                ]
              }
            }
          }
        }
      },
      "AssetSearchResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "site_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "address_summary": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AssetTreeNode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "site_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "parent_facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "path": {
            "type": "string"
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AssetTreeNode"
            }
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "error"
          },
          "message": {
            "type": "string"
          },
          "code": {
            "type": "integer"
          }
        }
      },
      "WhatsAppActionResponse": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "id": {
            "type": "string"
          },
          "error": {
            "type": "object",
            "nullable": true,
            "properties": {
              "message": {
                "type": "string"
              },
              "code": {
                "type": "integer"
              }
            }
          }
        }
      },
      "WhatsAppMessageInput": {
        "type": "object",
        "required": [
          "messaging_account_id",
          "to",
          "type"
        ],
        "properties": {
          "messaging_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "to": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "video",
              "audio",
              "document",
              "sticker"
            ]
          },
          "text": {
            "type": "string"
          },
          "media_url": {
            "type": "string",
            "format": "uri"
          },
          "media_id": {
            "type": "string"
          },
          "caption": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "context": {
            "type": "object"
          }
        }
      },
      "WhatsAppGroupInput": {
        "type": "object",
        "required": [
          "messaging_account_id",
          "group_name",
          "participants"
        ],
        "properties": {
          "messaging_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "group_name": {
            "type": "string"
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WhatsAppParticipantsInput": {
        "type": "object",
        "required": [
          "group_id",
          "participants"
        ],
        "properties": {
          "group_id": {
            "type": "string"
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WhatsAppPromoteInput": {
        "type": "object",
        "required": [
          "group_id",
          "participant"
        ],
        "properties": {
          "group_id": {
            "type": "string"
          },
          "participant": {
            "type": "string"
          }
        }
      },
      "InboxSource": {
        "type": "object",
        "properties": {
          "system": {
            "type": "string"
          },
          "message_id": {
            "type": "string",
            "nullable": true
          },
          "sender_id": {
            "type": "string",
            "nullable": true
          },
          "sender_name": {
            "type": "string",
            "nullable": true
          },
          "channel": {
            "type": "object",
            "properties": {
              "external_id": {
                "type": "string",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "InboxAi": {
        "type": "object",
        "properties": {
          "intent": {
            "type": "string",
            "enum": [
              "work_update",
              "problem",
              "unknown"
            ]
          },
          "confidence": {
            "type": "number",
            "format": "float"
          },
          "extracted": {
            "type": "object"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "provider": {
            "type": "string",
            "nullable": true
          },
          "ran_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboxFields": {
        "type": "object",
        "properties": {
          "site_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "category_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ],
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          }
        }
      },
      "InboxReview": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "needs_review",
              "ready"
            ]
          },
          "confidence": {
            "type": "number",
            "format": "float"
          },
          "confidence_bucket": {
            "type": "string",
            "enum": [
              "high",
              "medium",
              "low"
            ]
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "InboxAttachmentMeta": {
        "type": "object",
        "properties": {
          "storage_key": {
            "type": "string",
            "nullable": true
          },
          "thumbnail_key": {
            "type": "string",
            "nullable": true
          },
          "file_type": {
            "type": "string",
            "nullable": true
          },
          "file_size": {
            "type": "integer",
            "nullable": true
          },
          "file_name": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboxPayload": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "nullable": true
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "attachment_meta": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxAttachmentMeta"
            }
          },
          "attachment_names": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ai": {
            "$ref": "#/components/schemas/InboxAi"
          },
          "fields": {
            "$ref": "#/components/schemas/InboxFields"
          },
          "review": {
            "$ref": "#/components/schemas/InboxReview"
          },
          "source": {
            "$ref": "#/components/schemas/InboxSource"
          },
          "reporter": {
            "type": "object"
          },
          "metadata": {
            "type": "object"
          },
          "share": {
            "$ref": "#/components/schemas/InboxShare"
          }
        }
      },
      "InboxItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "channel_id": {
            "type": "string",
            "format": "uuid"
          },
          "messaging_account_id": {
            "type": "string",
            "format": "uuid"
          },
          "sender_phone": {
            "type": "string"
          },
          "member_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "message_type": {
            "type": "string"
          },
          "payload": {
            "$ref": "#/components/schemas/InboxPayload"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processed",
              "rejected"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboxChannelInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "group",
              "direct"
            ],
            "nullable": true
          },
          "purpose": {
            "type": "string",
            "enum": [
              "ops",
              "external",
              "read_only"
            ],
            "nullable": true
          },
          "platform": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboxSenderInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboxSourceInput": {
        "type": "object",
        "required": [
          "system"
        ],
        "properties": {
          "system": {
            "type": "string"
          },
          "message_id": {
            "type": "string",
            "nullable": true
          },
          "sender_id": {
            "type": "string",
            "nullable": true
          },
          "sender_name": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboxMessageInput": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboxAttachmentInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "nullable": true
          },
          "storage_key": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "mime_type": {
            "type": "string",
            "nullable": true
          },
          "size": {
            "type": "integer",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboxItemInput": {
        "type": "object",
        "required": [
          "organisation_id",
          "source"
        ],
        "properties": {
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "idempotency_key": {
            "type": "string",
            "nullable": true
          },
          "channel": {
            "$ref": "#/components/schemas/InboxChannelInput"
          },
          "source": {
            "$ref": "#/components/schemas/InboxSourceInput"
          },
          "sender": {
            "$ref": "#/components/schemas/InboxSenderInput"
          },
          "message": {
            "$ref": "#/components/schemas/InboxMessageInput"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboxAttachmentInput"
            }
          },
          "fields": {
            "$ref": "#/components/schemas/InboxFields"
          },
          "member_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "metadata": {
            "type": "object"
          }
        }
      },
      "InboxConversionInput": {
        "type": "object",
        "required": [
          "organisation_id",
          "type"
        ],
        "properties": {
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "work_update",
              "problem"
            ]
          },
          "site_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "category_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ],
            "nullable": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "submitted_by_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "reporter_member_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "reporter_phone": {
            "type": "string",
            "nullable": true
          },
          "force": {
            "type": "boolean",
            "nullable": true
          }
        }
      },
      "Issue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "channel_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "site_id": {
            "type": "string",
            "format": "uuid"
          },
          "facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "category_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "reporter_member_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "reporter_phone": {
            "type": "string",
            "nullable": true
          },
          "issue_reference": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "source_message_id": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RoutineLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "channel_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "site_id": {
            "type": "string",
            "format": "uuid"
          },
          "facility_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "category_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "submitted_by_id": {
            "type": "string",
            "format": "uuid"
          },
          "source_message_id": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "remarks": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboxShare": {
        "type": "object",
        "properties": {
          "inbox_guid": {
            "type": "string"
          },
          "work_update_guid": {
            "type": "string"
          },
          "problem_guid": {
            "type": "string"
          },
          "inbox_url": {
            "type": "string",
            "nullable": true
          },
          "work_update_url": {
            "type": "string",
            "nullable": true
          },
          "problem_url": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AuthLoginInput": {
        "type": "object",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "password": {
            "type": "string",
            "format": "password"
          },
          "organisation_id": {
            "type": "string",
            "nullable": true,
            "description": "Optional. One of the caller's active organisations (see the `organisations` array in the login response). Scopes the token to that workspace so the feature-gated resources (issues, sites, things, inventory, …) can resolve it; a token minted without it is user-scoped and those endpoints answer 403 `organisation_required`. An organisation the caller is not an active member of answers 422 `organisation_not_member`."
          }
        }
      },
      "AuthLoginResponse": {
        "type": "object",
        "properties": {
          "token_type": {
            "type": "string"
          },
          "access_token": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              }
            }
          },
          "organisations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganisationMembership"
            }
          },
          "organisation_id": {
            "type": "string",
            "nullable": true,
            "description": "The organisation the token is scoped to, or null for a user-scoped token."
          }
        }
      },
      "AuthMeResponse": {
        "type": "object",
        "properties": {
          "user": {
            "type": "object",
            "nullable": true
          },
          "token": {
            "type": "object",
            "nullable": true
          },
          "organisations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganisationMembership"
            }
          }
        }
      },
      "AuthLogoutResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "OrganisationMembership": {
        "type": "object",
        "properties": {
          "organisation_user_id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_name": {
            "type": "string",
            "nullable": true
          },
          "organisation_slug": {
            "type": "string",
            "nullable": true
          },
          "role_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "role_name": {
            "type": "string",
            "nullable": true
          },
          "role_display_name": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ClassificationResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "work_update",
              "problem",
              "general",
              "support"
            ]
          },
          "confidence": {
            "type": "number",
            "format": "float",
            "minimum": 0,
            "maximum": 1
          },
          "category": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "location": {
            "type": "string"
          },
          "things": {
            "type": "string"
          },
          "reasoning": {
            "type": "string"
          }
        }
      },
      "FuzzyMatchResult": {
        "type": "object",
        "properties": {
          "matches": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "confidence": {
                  "type": "number",
                  "format": "float"
                },
                "match_type": {
                  "type": "string",
                  "enum": [
                    "exact",
                    "contains",
                    "fuzzy"
                  ]
                }
              }
            }
          },
          "best_match": {
            "type": "object",
            "nullable": true
          }
        }
      },
      "ExtractedEntities": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "nullable": true
          },
          "things": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "person": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SeverityResult": {
        "type": "object",
        "properties": {
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "confidence": {
            "type": "number",
            "format": "float"
          },
          "reasoning": {
            "type": "string"
          }
        }
      },
      "CategorySuggestion": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string"
          },
          "confidence": {
            "type": "number",
            "format": "float"
          },
          "alternatives": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "NotificationResult": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string"
          },
          "channels": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "error": {
                  "type": "string"
                }
              }
            }
          },
          "success": {
            "type": "boolean"
          }
        }
      },
      "InventoryItem": {
        "type": "object",
        "description": "Admin-managed inventory item for CMMS spare parts, consumables, tools, PPE, and supplies.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "site_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "sku": {
            "type": "string",
            "nullable": true
          },
          "part_number": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "enum": [
              "spare_part",
              "consumable",
              "tool",
              "ppe",
              "cleaning_supply",
              "other"
            ]
          },
          "unit": {
            "type": "string"
          },
          "current_stock": {
            "type": "number",
            "format": "float"
          },
          "minimum_stock": {
            "type": "number",
            "format": "float"
          },
          "reorder_quantity": {
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "unit_cost": {
            "type": "number",
            "format": "float"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "obsolete"
            ]
          },
          "low_stock_alerted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "InventoryTransaction": {
        "type": "object",
        "description": "Append-only inventory ledger row created for receipts, issues, adjustments, transfers, and work-order consumption.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "inventory_item_id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "issue_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "receipt",
              "issue",
              "adjustment",
              "transfer"
            ]
          },
          "quantity": {
            "type": "number",
            "format": "float"
          },
          "stock_before": {
            "type": "number",
            "format": "float"
          },
          "stock_after": {
            "type": "number",
            "format": "float"
          },
          "unit_cost": {
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorkOrderPartConsumption": {
        "type": "object",
        "description": "Work-order part usage. consumed_at is set after inventory deduction succeeds so repeat completions do not double-deduct stock.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "issue_id": {
            "type": "string",
            "format": "uuid"
          },
          "inventory_item_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "part_name": {
            "type": "string"
          },
          "part_number": {
            "type": "string",
            "nullable": true
          },
          "quantity_planned": {
            "type": "number",
            "format": "float"
          },
          "quantity_used": {
            "type": "number",
            "format": "float"
          },
          "unit_cost": {
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "consumed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "LocationTree": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "path": {
            "type": "string"
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LocationTree"
            }
          }
        }
      },
      "AiAssistantResponse": {
        "type": "object",
        "properties": {
          "response": {
            "type": "string"
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "confidence": {
            "type": "number",
            "format": "float"
          }
        }
      },
      "ByonAccountHealth": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "healthy",
              "degraded",
              "unreachable"
            ]
          },
          "last_checked_at": {
            "type": "string",
            "format": "date-time"
          },
          "phone_number_id": {
            "type": "string"
          },
          "waba_id": {
            "type": "string"
          },
          "webhook_configured": {
            "type": "boolean"
          }
        }
      },
      "ComplianceRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed",
              "verified",
              "rejected"
            ]
          },
          "assigned_to_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "due_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "verified_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "verified_by_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "rejected_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejected_by_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "rejection_reason": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaginatedComplianceRecords": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceRecord"
            }
          },
          "links": {
            "$ref": "#/components/schemas/PaginationLinks"
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "ComplianceSummary": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "pending": {
            "type": "integer"
          },
          "completed": {
            "type": "integer"
          },
          "verified": {
            "type": "integer"
          },
          "rejected": {
            "type": "integer"
          },
          "overdue": {
            "type": "integer"
          },
          "completion_rate": {
            "type": "number",
            "format": "float"
          },
          "verification_rate": {
            "type": "number",
            "format": "float"
          }
        }
      },
      "ComplianceHistoryEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "compliance_id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string"
          },
          "performed_by_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "performed_by_name": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ComplianceCompletionInput": {
        "type": "object",
        "description": "Mobile completion payload. Per-item statuses for NUMBER items are derived server-side from `value` and any client-supplied `status` is ignored (checklist threshold rules, blueprint 20260817). The legacy `checked` boolean is accepted for ONE release after 2026-08-17 and translated to `status`; it is logged as deprecated on every use.",
        "properties": {
          "checklist_results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceChecklistAnswer"
            }
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "routine_log_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "evidence_count": {
            "type": "integer",
            "minimum": 0,
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "whatsapp",
              "manual",
              "api",
              "import",
              "public_qr",
              "public_link"
            ],
            "nullable": true
          }
        }
      },
      "KnowledgeBaseResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "question": {
            "type": "string"
          },
          "answer": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "relevance_score": {
            "type": "number",
            "format": "float"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MobileDashboardResponse": {
        "type": "object",
        "properties": {
          "open_issues": {
            "type": "integer"
          },
          "pending_updates": {
            "type": "integer"
          },
          "recent_activity": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "due_today": {
            "type": "integer"
          },
          "overdue": {
            "type": "integer"
          }
        }
      },
      "MobileUpdate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "site_name": {
            "type": "string",
            "nullable": true
          },
          "facility_name": {
            "type": "string",
            "nullable": true
          },
          "category_name": {
            "type": "string",
            "nullable": true
          },
          "remarks": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "submitted_by_name": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MobileProblem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "issue_reference": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "site_name": {
            "type": "string",
            "nullable": true
          },
          "facility_name": {
            "type": "string",
            "nullable": true
          },
          "category_name": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MobileChatMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "message": {
            "type": "string"
          },
          "sender_id": {
            "type": "string",
            "format": "uuid"
          },
          "sender_name": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NotificationPreference": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "organisation_id": {
            "type": "string",
            "format": "uuid"
          },
          "channels": {
            "type": "object",
            "properties": {
              "whatsapp": {
                "type": "boolean"
              },
              "email": {
                "type": "boolean"
              },
              "push": {
                "type": "boolean"
              }
            }
          },
          "notify_on": {
            "type": "object",
            "properties": {
              "work_update_verified": {
                "type": "boolean"
              },
              "work_update_rejected": {
                "type": "boolean"
              },
              "problem_assigned": {
                "type": "boolean"
              },
              "problem_resolved": {
                "type": "boolean"
              },
              "reminder": {
                "type": "boolean"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NotificationPreferenceInput": {
        "type": "object",
        "properties": {
          "channels": {
            "type": "object",
            "properties": {
              "whatsapp": {
                "type": "boolean"
              },
              "email": {
                "type": "boolean"
              },
              "push": {
                "type": "boolean"
              }
            }
          },
          "notify_on": {
            "type": "object",
            "properties": {
              "work_update_verified": {
                "type": "boolean"
              },
              "work_update_rejected": {
                "type": "boolean"
              },
              "problem_assigned": {
                "type": "boolean"
              },
              "problem_resolved": {
                "type": "boolean"
              },
              "reminder": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "LeaderboardEntry": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "user_name": {
            "type": "string"
          },
          "total_updates": {
            "type": "integer"
          },
          "total_issues": {
            "type": "integer"
          },
          "verified_count": {
            "type": "integer"
          },
          "score": {
            "type": "number",
            "format": "float"
          },
          "rank": {
            "type": "integer"
          }
        }
      },
      "PricingPlan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "format": "float"
          },
          "currency": {
            "type": "string",
            "example": "MYR"
          },
          "interval": {
            "type": "string",
            "enum": [
              "monthly",
              "yearly"
            ]
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_popular": {
            "type": "boolean"
          }
        }
      },
      "OtpResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "retry_after": {
            "type": "integer"
          }
        }
      },
      "ComplianceChecklistAnswer": {
        "type": "object",
        "required": [
          "item_id"
        ],
        "properties": {
          "item_id": {
            "type": "string",
            "description": "The template item's stable id. A uuid on templates authored since 2026-08-17; older templates carry integers, and answers generated before the ids existed are matched by position."
          },
          "status": {
            "type": "string",
            "enum": [
              "compliant",
              "non_compliant",
              "not_applicable",
              "needs_attention"
            ],
            "nullable": true,
            "description": "Required for non-number items. IGNORED for number items — the server derives it from `value`."
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "The reading for a number item. Accepts '.' or ',' as the decimal separator; stored as a canonical float."
          },
          "remarks": {
            "type": "string",
            "nullable": true
          },
          "checked": {
            "type": "boolean",
            "nullable": true,
            "deprecated": true,
            "description": "DEPRECATED legacy shape (removed one release after 2026-08-17). Translated to status: true -> compliant, false -> non_compliant."
          },
          "notes": {
            "type": "string",
            "nullable": true,
            "deprecated": true,
            "description": "DEPRECATED alias of `remarks`, honoured only alongside `checked`."
          }
        }
      },
      "ComplianceCompletionResult": {
        "type": "object",
        "properties": {
          "record_id": {
            "type": "string",
            "format": "uuid"
          },
          "completed_at": {
            "type": "string"
          },
          "next_due_date": {
            "type": "string",
            "format": "date"
          },
          "was_on_time": {
            "type": "boolean"
          },
          "days_late": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "description": "The DERIVED record status: 'failed' when any item is non_compliant/needs_attention, else 'completed'. A template with no typed items always completes, exactly as before the module."
          },
          "checklist_results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceChecklistAnswer"
            },
            "description": "The answers AS STORED, with derived statuses and canonical values."
          },
          "issues": {
            "type": "array",
            "description": "Issues created or updated by out-of-range readings in this submission.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "reference": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "low",
                    "medium",
                    "high",
                    "critical"
                  ]
                }
              }
            }
          }
        }
      },
      "InventoryStockTake": {
        "type": "object",
        "description": "A stock-take / cycle-count session. InventoryStockTakeService is the only writer; completed and cancelled sessions are immutable.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string",
            "example": "ST-20260819-K4PZ"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "counting",
              "review",
              "completed",
              "cancelled"
            ]
          },
          "site": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "line_count": {
            "type": "integer"
          },
          "counted_count": {
            "type": "integer"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancelled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InventoryStockTakeLine"
            }
          }
        }
      },
      "InventoryStockTakeLine": {
        "type": "object",
        "description": "One item inside a session. expected_quantity snapshots at COUNT time; adjustment happens only at completion, as a relative delta.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "counted",
              "skipped"
            ]
          },
          "expected_quantity": {
            "type": "number",
            "nullable": true
          },
          "counted_quantity": {
            "type": "number",
            "nullable": true
          },
          "variance": {
            "type": "number",
            "nullable": true
          },
          "counted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "item": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "sku": {
                "type": "string",
                "nullable": true
              },
              "barcode": {
                "type": "string",
                "nullable": true
              },
              "unit": {
                "type": "string"
              },
              "storage_site": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "CredentialRefusal": {
        "type": "object",
        "description": "Task H5 (2026-09-29): a credential refused for the workspace or member it tried to act on. Task H6 added `platform_number_forbidden` (a workspace credential asked for the platform WhatsApp number — directly, or through an account that uses platform credentials such as the seeded \"Laksana Default Number\") and `permission_denied` (the member behind the credential lacks the console permission for the action). The platform master key never receives these; see laksana-core docs/04-API-REFERENCE.md §2 \"Which workspace a token acts on, and as whom\".",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_code": {
            "type": "string",
            "enum": [
              "organisation_mismatch",
              "organisation_required",
              "platform_key_required",
              "member_required",
              "member_mismatch",
              "not_a_member",
              "platform_number_forbidden",
              "permission_denied"
            ]
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "error_code",
          "message"
        ]
      },
      "PlatformFieldRefusal": {
        "type": "object",
        "description": "Task H6 (2026-09-29): a workspace credential tried to CHANGE a field only the platform sets. Sending the current value back is accepted.",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_code": {
            "type": "string",
            "enum": [
              "platform_field_forbidden"
            ]
          },
          "message": {
            "type": "string"
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "status",
              "settings.limits"
            ]
          }
        },
        "required": [
          "error_code",
          "message",
          "fields"
        ]
      },
      "PhoneNumberIdRefusal": {
        "type": "object",
        "description": "Task H6 review: the platform's own phone number id, or one another organisation already holds, cannot be registered to a workspace (every caller).",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_code": {
            "type": "string",
            "enum": [
              "phone_number_id_unavailable"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "platform_number",
              "held_by_another_organisation"
            ]
          },
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object"
          }
        },
        "required": [
          "error_code",
          "reason"
        ]
      },
      "WorkOrder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "The issue status the work-order status maps to."
          },
          "work_order_status": {
            "type": "string",
            "enum": [
              "requested",
              "pending_approval",
              "approved",
              "scheduled",
              "in_progress",
              "on_hold",
              "completed",
              "closed",
              "rejected",
              "cancelled"
            ]
          },
          "work_order_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ]
          },
          "approval_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "site": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "asset": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "assignee": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "team": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "is_external": {
                "type": "boolean"
              }
            }
          },
          "planned_start_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "planned_end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sla_due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "overdue": {
            "type": "boolean"
          },
          "tasks": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "completed": {
                "type": "integer"
              }
            }
          },
          "estimated_cost": {
            "type": [
              "string",
              "null"
            ],
            "description": "Always null for a vendor."
          },
          "actual_cost": {
            "type": [
              "string",
              "null"
            ],
            "description": "Always null for a vendor."
          },
          "allowed_transitions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The moves THIS member may make now (the console's rule)."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      }
    }
  },
  "x-laksana-cmms-inventory": {
    "entitlement": "cmms_inventory",
    "admin_url": "/admin/{tenant}/inventory-items",
    "strict_consumption_setting": "organisation.settings.cmms.inventory_strict_consumption",
    "plan_availability": [
      "advanced",
      "enterprise"
    ]
  }
}
