{
  "openapi": "3.0.3",
  "info": {
    "title": "Quardlyn CRM API",
    "version": "v1",
    "description": "Public CRM API contract for third-party property CRM integrations. Version v1 is STABLE (API Contract Frozen) as of 2026-07-03. Developers can build against v1, new backward-compatible features may be added, and no public API contract changes may be made to v1 unless they are backward compatible or approved as a breaking v2 change.\n"
  },
  "servers": [
    {
      "url": "https://{host}/api/v1/crm",
      "variables": {
        "host": {
          "default": "example.quardlyn.test"
        }
      }
    }
  ],
  "security": [
    {
      "CrmKey": [],
      "CrmTimestamp": [],
      "CrmNonce": [],
      "CrmSignature": []
    }
  ],
  "tags": [
    {
      "name": "Metadata"
    },
    {
      "name": "Properties"
    },
    {
      "name": "Sandbox"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "Check CRM API availability",
        "security": [],
        "responses": {
          "200": {
            "description": "API health",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/capabilities": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "Return public CRM API capabilities",
        "security": [],
        "responses": {
          "200": {
            "description": "Public capabilities",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CapabilitiesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/version": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "Return public CRM API version metadata",
        "security": [],
        "responses": {
          "200": {
            "description": "Version metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VersionResponse"
                }
              }
            }
          }
        }
      }
    },
    "/properties": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "List properties for the authenticated CRM connection",
        "description": "Live Production GET endpoint. Returns a lightweight summary payload by default. Use expand=full to request complete canonical property objects. Results are scoped to the authenticated CRM connection; properties outside that scope must not be returned.\n",
        "operationId": "listProperties",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          },
          {
            "$ref": "#/components/parameters/StatusFilter"
          },
          {
            "$ref": "#/components/parameters/MarketingStatusFilter"
          },
          {
            "$ref": "#/components/parameters/DepartmentFilter"
          },
          {
            "$ref": "#/components/parameters/PublishedFilter"
          },
          {
            "$ref": "#/components/parameters/IncludeArchived"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedSince"
          },
          {
            "$ref": "#/components/parameters/SyncToken"
          },
          {
            "$ref": "#/components/parameters/Fields"
          },
          {
            "$ref": "#/components/parameters/Expand"
          },
          {
            "$ref": "#/components/parameters/Sort"
          },
          {
            "$ref": "#/components/parameters/Order"
          },
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "Property page for the authenticated CRM connection",
            "headers": {
              "X-API-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyListResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified. Returned when If-None-Match matches the current collection representation."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Properties"
        ],
        "summary": "Create or upsert a property",
        "operationId": "createProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/ProviderRequestId"
          },
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Property accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/properties/{external_ref}": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "Retrieve a full canonical property by external reference",
        "description": "Live Production GET endpoint. Returns the full canonical property payload by default. Access is scoped to the authenticated CRM connection. If the property exists but does not belong to the connection, return 404 to avoid leaking existence.\n",
        "operationId": "getPropertyByExternalRef",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/Fields"
          },
          {
            "$ref": "#/components/parameters/Expand"
          },
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "Full canonical property payload",
            "headers": {
              "X-API-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyGetResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified. Returned when If-None-Match matches the current property representation."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Properties"
        ],
        "summary": "Replace or upsert a property by external reference",
        "operationId": "updateProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/ProviderRequestId"
          },
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Property updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Properties"
        ],
        "summary": "Partially update or upsert a property by external reference",
        "operationId": "patchProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/ProviderRequestId"
          },
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Property updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Properties"
        ],
        "summary": "Delete or inactivate a property",
        "operationId": "deleteProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/RequestId"
          },
          {
            "$ref": "#/components/parameters/ProviderRequestId"
          },
          {
            "$ref": "#/components/parameters/EventId"
          }
        ],
        "responses": {
          "200": {
            "description": "Property deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/properties/{external_ref}/withdraw": {
      "post": {
        "tags": [
          "Properties"
        ],
        "summary": "Withdraw a property",
        "operationId": "withdrawProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/properties/{external_ref}/archive": {
      "post": {
        "tags": [
          "Properties"
        ],
        "summary": "Archive a property",
        "operationId": "archiveProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/properties/{external_ref}/restore": {
      "post": {
        "tags": [
          "Properties"
        ],
        "summary": "Restore an archived property",
        "operationId": "restoreProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/properties/{external_ref}/reactivate": {
      "post": {
        "tags": [
          "Properties"
        ],
        "summary": "Reactivate a property",
        "operationId": "reactivateProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/properties/lookup": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "Retrieve a property by explicit identifier type",
        "description": "Live Production GET endpoint. Use this endpoint when looking up by immutable property_uid, or when external references may have changed. The endpoint does not guess identifier type; id_type and value are required.\n",
        "operationId": "lookupProperty",
        "parameters": [
          {
            "name": "id_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "external_ref",
                "property_uid"
              ]
            }
          },
          {
            "name": "value",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Fields"
          },
          {
            "$ref": "#/components/parameters/Expand"
          },
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "Full canonical property payload",
            "headers": {
              "X-API-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyGetResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/properties/count": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "Reserved v1 contract: count properties matching filters",
        "description": "Reserved for future efficient CRM synchronization and dashboards.",
        "operationId": "countProperties",
        "parameters": [
          {
            "$ref": "#/components/parameters/StatusFilter"
          },
          {
            "$ref": "#/components/parameters/MarketingStatusFilter"
          },
          {
            "$ref": "#/components/parameters/DepartmentFilter"
          },
          {
            "$ref": "#/components/parameters/PublishedFilter"
          },
          {
            "$ref": "#/components/parameters/IncludeArchived"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/CreatedSince"
          },
          {
            "$ref": "#/components/parameters/SyncToken"
          }
        ],
        "responses": {
          "200": {
            "description": "Count result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyCountResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/properties/changes": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "Reserved v1 contract: incremental property changes",
        "description": "Reserved for future sync_token based incremental synchronization.",
        "operationId": "listPropertyChanges",
        "parameters": [
          {
            "$ref": "#/components/parameters/SyncToken"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          },
          {
            "$ref": "#/components/parameters/Fields"
          },
          {
            "$ref": "#/components/parameters/Expand"
          }
        ],
        "responses": {
          "200": {
            "description": "Incremental property changes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/properties/deleted": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "Reserved v1 contract: deleted or withdrawn property references",
        "description": "Reserved for future downstream CRM deletion reconciliation.",
        "operationId": "listDeletedProperties",
        "parameters": [
          {
            "$ref": "#/components/parameters/SyncToken"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted property references",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedPropertyListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/properties/schema": {
      "get": {
        "tags": [
          "Properties"
        ],
        "summary": "Reserved v1 contract: canonical property schema metadata",
        "description": "Reserved for future clients that need runtime discovery of supported fields, enums, limits, media capabilities, ownership metadata and canonical schema version.\n",
        "operationId": "getPropertySchema",
        "responses": {
          "200": {
            "description": "Canonical schema capability metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertySchemaResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/sandbox/crm/properties": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "get": {
        "tags": [
          "Sandbox"
        ],
        "summary": "List sandbox properties for certification and sync testing",
        "operationId": "sandboxListProperties",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox property page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxPropertyListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Create or upsert a sandbox property using the canonical schema",
        "operationId": "sandboxCreateProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sandbox property created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxPropertyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      }
    },
    "/sandbox/crm/properties/{external_ref}": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "put": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Replace or upsert a sandbox property",
        "operationId": "sandboxUpdateProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sandbox property updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxPropertyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      },
      "patch": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Partially update a sandbox property",
        "operationId": "sandboxPatchProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sandbox property updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxPropertyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      },
      "delete": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Delete a sandbox property",
        "operationId": "sandboxDeleteProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/properties/{external_ref}/withdraw": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Withdraw a sandbox property",
        "operationId": "sandboxWithdrawProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/properties/{external_ref}/archive": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Archive a sandbox property",
        "operationId": "sandboxArchiveProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/properties/{external_ref}/restore": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Restore a sandbox property",
        "operationId": "sandboxRestoreProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/properties/{external_ref}/reactivate": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Reactivate a sandbox property",
        "operationId": "sandboxReactivateProperty",
        "parameters": [
          {
            "$ref": "#/components/parameters/ExternalRef"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/sync": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "get": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Run sandbox initial or delta sync checks",
        "operationId": "sandboxSync",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox sync page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxPropertyListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/webhooks": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "get": {
        "tags": [
          "Sandbox"
        ],
        "summary": "List sandbox webhook simulations generated during certification",
        "operationId": "sandboxListWebhooks",
        "responses": {
          "200": {
            "description": "Sandbox webhook simulation list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxWebhookListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/sandbox/crm/webhooks/{webhook_id}/replay": {
      "servers": [
        {
          "url": "https://{host}/api/v1",
          "variables": {
            "host": {
              "default": "example.quardlyn.test"
            }
          }
        }
      ],
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Replay a sandbox webhook simulation",
        "operationId": "sandboxReplayWebhook",
        "parameters": [
          {
            "name": "webhook_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Success"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "CrmKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CRM-Key"
      },
      "CrmTimestamp": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CRM-Timestamp"
      },
      "CrmNonce": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CRM-Nonce"
      },
      "CrmSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CRM-Signature"
      }
    },
    "parameters": {
      "ExternalRef": {
        "name": "external_ref",
        "in": "path",
        "required": true,
        "description": "URL-encoded, case-sensitive external property reference unique per authenticated connection.",
        "schema": {
          "type": "string",
          "maxLength": 191,
          "pattern": "^[^\\x00-\\x1F\\x7F\\/?#]+$"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size. Default 50, maximum 100.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "description": "Zero-based page offset.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "StatusFilter": {
        "name": "status",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "MarketingStatusFilter": {
        "name": "marketing_status",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "draft",
            "available",
            "under_offer",
            "sold_stc",
            "sold",
            "let_agreed",
            "let",
            "withdrawn",
            "archived"
          ]
        }
      },
      "DepartmentFilter": {
        "name": "department",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "sale",
            "let",
            "rent",
            "auction",
            "commercial"
          ]
        }
      },
      "PublishedFilter": {
        "name": "published",
        "in": "query",
        "required": false,
        "schema": {
          "type": "boolean"
        }
      },
      "IncludeArchived": {
        "name": "include_archived",
        "in": "query",
        "required": false,
        "description": "Archived/deleted records are hidden by default.",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "UpdatedSince": {
        "name": "updated_since",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "CreatedSince": {
        "name": "created_since",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "SyncToken": {
        "name": "sync_token",
        "in": "query",
        "required": false,
        "description": "Opaque token returned by previous list/change responses for incremental sync.",
        "schema": {
          "type": "string"
        }
      },
      "Fields": {
        "name": "fields",
        "in": "query",
        "required": false,
        "description": "Comma-separated canonical fields to include. Unknown fields return validation_failed.",
        "schema": {
          "type": "string",
          "example": "property_uid,external_ref,title,price,status"
        }
      },
      "Expand": {
        "name": "expand",
        "in": "query",
        "required": false,
        "description": "Comma-separated expansion set. Allowed values are media, documents, ownership, rooms, virtual_tour and full. Collection defaults to summary; expand=full returns full canonical objects.",
        "schema": {
          "type": "string",
          "example": "media,documents,ownership"
        }
      },
      "Sort": {
        "name": "sort",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "updated_at",
            "created_at",
            "price",
            "title"
          ],
          "default": "updated_at"
        }
      },
      "Order": {
        "name": "order",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ],
          "default": "desc"
        }
      },
      "IfNoneMatch": {
        "name": "If-None-Match",
        "in": "header",
        "required": false,
        "description": "ETag value from a previous response. Matching resources return 304 Not Modified.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "RequestId": {
        "name": "X-Request-Id",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "ProviderRequestId": {
        "name": "X-Provider-Request-Id",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "EventId": {
        "name": "X-Event-Id",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Success": {
        "description": "Successful operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SuccessResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid CRM authentication.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not permitted.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found for the authenticated connection.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "Duplicate request, already processing request, or property conflict.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "Request validation failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "InternalError": {
        "description": "Internal processing error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Request body exceeds the sandbox or production payload limit.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "PropertyGetResponse": {
        "type": "object",
        "required": [
          "success",
          "api_version",
          "environment",
          "external_ref",
          "property",
          "request_id"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "environment": {
            "type": "string",
            "example": "production"
          },
          "external_ref": {
            "type": "string",
            "example": "CRM-SALE-001"
          },
          "property": {
            "$ref": "#/components/schemas/Property"
          },
          "meta": {
            "type": "object",
            "properties": {
              "retrieved_at": {
                "type": "string",
                "format": "date-time"
              },
              "property_uid": {
                "type": "string"
              },
              "managed_mode": {
                "type": "string",
                "enum": [
                  "CRM_MASTER",
                  "HYBRID",
                  "PORTAL_MASTER"
                ]
              },
              "ownership_source": {
                "type": "string"
              },
              "last_sync_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "request_id": {
            "type": "string",
            "example": "req_abc123"
          }
        }
      },
      "PropertySummary": {
        "type": "object",
        "description": "Lightweight collection item returned by GET /properties unless expand=full is requested.",
        "properties": {
          "property_uid": {
            "type": "string"
          },
          "external_ref": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "display_address": {
            "type": "string"
          },
          "department": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "marketing_status": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "price_display": {
            "type": "string"
          },
          "rent_price": {
            "type": "number"
          },
          "rent_frequency": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "published": {
            "type": "boolean"
          },
          "thumbnail": {
            "type": "string",
            "format": "uri"
          },
          "has_virtual_tour": {
            "type": "boolean"
          },
          "has_floorplan": {
            "type": "boolean"
          },
          "has_brochure": {
            "type": "boolean"
          },
          "has_epc": {
            "type": "boolean"
          },
          "managed_mode": {
            "type": "string",
            "enum": [
              "CRM_MASTER",
              "HYBRID",
              "PORTAL_MASTER"
            ]
          },
          "ownership_source": {
            "type": "string"
          }
        }
      },
      "PropertyListResponse": {
        "type": "object",
        "required": [
          "success",
          "api_version",
          "environment",
          "data",
          "pagination",
          "request_id"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "environment": {
            "type": "string",
            "example": "production"
          },
          "data": {
            "type": "array",
            "description": "PropertySummary by default; full Property objects when expand=full.",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/PropertySummary"
                },
                {
                  "$ref": "#/components/schemas/Property"
                }
              ]
            }
          },
          "pagination": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer",
                "example": 50
              },
              "offset": {
                "type": "integer",
                "example": 0
              },
              "count": {
                "type": "integer",
                "example": 50
              },
              "has_more": {
                "type": "boolean",
                "example": true
              },
              "next_offset": {
                "type": "integer",
                "example": 50
              }
            }
          },
          "filters": {
            "type": "object",
            "additionalProperties": true
          },
          "sync": {
            "type": "object",
            "properties": {
              "next_sync_token": {
                "type": "string"
              },
              "watermark": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "request_id": {
            "type": "string",
            "example": "req_abc123"
          }
        }
      },
      "PropertyCountResponse": {
        "type": "object",
        "required": [
          "success",
          "api_version",
          "environment",
          "count",
          "request_id"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "environment": {
            "type": "string",
            "example": "production"
          },
          "count": {
            "type": "integer",
            "example": 123
          },
          "filters": {
            "type": "object",
            "additionalProperties": true
          },
          "request_id": {
            "type": "string",
            "example": "req_abc123"
          }
        }
      },
      "DeletedProperty": {
        "type": "object",
        "properties": {
          "property_uid": {
            "type": "string"
          },
          "external_ref": {
            "type": "string"
          },
          "deleted_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "example": "deleted"
          }
        }
      },
      "DeletedPropertyListResponse": {
        "type": "object",
        "required": [
          "success",
          "api_version",
          "environment",
          "data",
          "pagination",
          "request_id"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "environment": {
            "type": "string",
            "example": "production"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeletedProperty"
            }
          },
          "pagination": {
            "type": "object",
            "additionalProperties": true
          },
          "sync": {
            "type": "object",
            "properties": {
              "next_sync_token": {
                "type": "string"
              },
              "watermark": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "request_id": {
            "type": "string",
            "example": "req_abc123"
          }
        }
      },
      "PropertySchemaResponse": {
        "type": "object",
        "required": [
          "success",
          "version",
          "canonical_fields",
          "capabilities"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "version": {
            "type": "string",
            "example": "v1"
          },
          "canonical_fields": {
            "type": "integer",
            "example": 371
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "supports_virtual_tours": {
                "type": "boolean"
              },
              "supports_documents": {
                "type": "boolean"
              },
              "supports_rooms": {
                "type": "boolean"
              },
              "supports_ownership": {
                "type": "boolean"
              },
              "supports_media": {
                "type": "boolean"
              },
              "supports_field_selection": {
                "type": "boolean"
              },
              "supports_etags": {
                "type": "boolean"
              },
              "supports_sync_token": {
                "type": "boolean"
              }
            }
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enums": {
            "type": "object",
            "additionalProperties": true
          },
          "limits": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "PropertyRequest": {
        "type": "object",
        "required": [
          "property"
        ],
        "properties": {
          "request_id": {
            "type": "string"
          },
          "provider_request_id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "idempotency_key": {
            "type": "string"
          },
          "property": {
            "$ref": "#/components/schemas/Property"
          }
        }
      },
      "Property": {
        "type": "object",
        "description": "Quardlyn canonical property import model. This is the same flat canonical property object used by the Feed Engine for BLM, CSV and XML imports. Nested CRM aliases may be accepted internally for legacy clients, but they are not the public schema.\n",
        "required": [
          "external_ref",
          "department",
          "display_address"
        ],
        "additionalProperties": false,
        "properties": {
          "external_ref": {
            "type": "string",
            "maxLength": 191,
            "description": "Required. Provider property reference unique within the authenticated connection.",
            "example": "CRM-SALE-001"
          },
          "status_id": {
            "type": "integer",
            "description": "Optional. Import status code. 1 available, 2 under offer, 3 sold STC, 4 sold, 5 inactive.",
            "enum": [
              0,
              1,
              2,
              3,
              4,
              5
            ],
            "example": 1
          },
          "trans_type_id": {
            "type": "integer",
            "description": "Optional. Transaction type. 1 sale, 2 let, 3 auction, 4 commercial.",
            "enum": [
              1,
              2,
              3,
              4
            ],
            "example": 1
          },
          "category": {
            "type": "string",
            "description": "Optional. Provider category such as residential, commercial, land, new_homes, student or shared_ownership.",
            "example": "residential"
          },
          "department": {
            "type": "string",
            "description": "Required. Canonical department imported to uk_pfor.",
            "enum": [
              "sale",
              "let",
              "rent",
              "auction",
              "commercial"
            ],
            "example": "sale"
          },
          "marketing_status": {
            "type": "string",
            "description": "Optional. Public listing state used with published to choose LIVE or DRAFT import state.",
            "enum": [
              "draft",
              "available",
              "under_offer",
              "sold_stc",
              "sold",
              "let_agreed",
              "let",
              "withdrawn",
              "archived"
            ],
            "example": "available"
          },
          "status": {
            "type": "string",
            "description": "Optional. Provider status label accepted alongside marketing_status/status_id.",
            "example": "For Sale"
          },
          "published": {
            "type": "boolean",
            "description": "Optional. true imports as LIVE when validation passes; false or omitted imports as DRAFT.",
            "example": true
          },
          "title": {
            "type": "string",
            "description": "Optional. Public listing title. display_address remains required for ingestion.",
            "example": "Three-bedroom house near the station"
          },
          "display_address": {
            "type": "string",
            "description": "Required. Public display title/address.",
            "example": "10 High Street, London"
          },
          "address_1": {
            "type": "string",
            "description": "Optional. Address line 1.",
            "example": "10 High Street"
          },
          "address_2": {
            "type": "string",
            "description": "Optional. Address line 2.",
            "example": "Westminster"
          },
          "address_3": {
            "type": "string",
            "description": "Optional. Address line 3/locality.",
            "example": "St James"
          },
          "address_4": {
            "type": "string",
            "description": "Optional. Address line 4.",
            "example": "Greater London"
          },
          "town": {
            "type": "string",
            "description": "Optional. Postal town/city.",
            "example": "London"
          },
          "county": {
            "type": "string",
            "description": "Optional. County/area.",
            "example": "Greater London"
          },
          "country": {
            "type": "string",
            "description": "Optional. Country name. Defaults internally to United Kingdom when omitted.",
            "example": "United Kingdom"
          },
          "postcode": {
            "type": "string",
            "description": "Required for live publishing. Full postcode; may be generated from postcode1 and postcode2.",
            "example": "SW1A 1AA"
          },
          "postcode1": {
            "type": "string",
            "description": "Optional. Outward postcode component for feeds that split postcode.",
            "example": "SW1A"
          },
          "postcode2": {
            "type": "string",
            "description": "Optional. Inward postcode component for feeds that split postcode.",
            "example": "1AA"
          },
          "latitude": {
            "type": "number",
            "format": "double",
            "description": "Optional. Decimal latitude. Alias lat is also accepted internally.",
            "example": 51.501
          },
          "longitude": {
            "type": "number",
            "format": "double",
            "description": "Optional. Decimal longitude. Alias lng is also accepted internally.",
            "example": -0.141
          },
          "lat": {
            "type": "number",
            "format": "double",
            "description": "Optional. Feed-engine coordinate field equivalent to latitude.",
            "example": 51.501
          },
          "lng": {
            "type": "number",
            "format": "double",
            "description": "Optional. Feed-engine coordinate field equivalent to longitude.",
            "example": -0.141
          },
          "property_type": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ],
            "description": "Optional. Internal property main type id or canonical type label.",
            "example": "Residential Property"
          },
          "property_subtype": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ],
            "description": "Optional. Internal subtype id or provider subtype value resolved by import mapping.",
            "example": 101
          },
          "property_sub_type": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ],
            "description": "Optional. Alternate canonical subtype spelling accepted by the API.",
            "example": "terraced"
          },
          "beds": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional. Bedroom count.",
            "example": 3
          },
          "bedrooms": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional. Bedroom count alias accepted by the API.",
            "example": 3
          },
          "baths": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional. Bathroom count.",
            "example": 2
          },
          "bathrooms": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional. Bathroom count alias accepted by the API.",
            "example": 2
          },
          "receptions": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional. Reception/living room count.",
            "example": 1
          },
          "price": {
            "type": "number",
            "description": "Required for live sale, let and auction unless price_display is supplied. Sale price, rent amount or auction guide price.",
            "example": 525000
          },
          "sale_price": {
            "type": "number",
            "description": "Optional. Sale-specific price when a feed separates sale, rent and auction prices.",
            "example": 525000
          },
          "rent_price": {
            "type": "number",
            "description": "Optional. Lettings-specific rent amount when supplied separately from price.",
            "example": 1650
          },
          "auction_price": {
            "type": "number",
            "description": "Optional. Auction guide/reserve amount when supplied separately from price.",
            "example": 300000
          },
          "currency": {
            "type": "string",
            "description": "Optional. ISO-style currency code or provider currency label.",
            "example": "GBP"
          },
          "price_display": {
            "type": "string",
            "description": "Optional. Display price text such as POA or Offers over 525000.",
            "example": "Offers over GBP 525,000"
          },
          "price_qualifier": {
            "type": "string",
            "description": "Optional. Provider price qualifier.",
            "example": "offers_over"
          },
          "rent_frequency": {
            "type": "string",
            "description": "Optional for lettings. Rent period.",
            "enum": [
              "PCM",
              "PW",
              "pcm",
              "pw",
              "1",
              "2"
            ],
            "example": "PCM"
          },
          "deposit": {
            "type": "number",
            "description": "Optional. Lettings deposit/bond amount.",
            "example": 1673
          },
          "available_from": {
            "type": "string",
            "format": "date",
            "description": "Optional. Lettings available date.",
            "example": "2026-08-01"
          },
          "tenure": {
            "type": "string",
            "description": "Optional. Sale/auction tenure label.",
            "enum": [
              "Freehold",
              "Leasehold",
              "Share of Freehold",
              "Commonhold",
              "Unknown",
              "freehold",
              "leasehold",
              "share_of_freehold",
              "commonhold",
              "unknown"
            ],
            "example": "Freehold"
          },
          "tenure_type_id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. Tenure code. 1 Freehold, 2 Leasehold, or tenure label.",
            "example": 1
          },
          "new_home_flag": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. New build/new home flag.",
            "example": false
          },
          "new_build": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. New build flag alias accepted by the API.",
            "example": false
          },
          "furnished": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. Furnishing state for lettings.",
            "example": "Furnished"
          },
          "leasehold_years": {
            "type": "number",
            "description": "Optional. Remaining lease length in years.",
            "example": 998
          },
          "service_charge": {
            "type": "number",
            "description": "Optional. Leasehold or building service charge.",
            "example": 1200
          },
          "ground_rent": {
            "type": "number",
            "description": "Optional. Leasehold ground rent.",
            "example": 250
          },
          "council_tax_band": {
            "type": "string",
            "description": "Optional. Council tax band.",
            "example": "D"
          },
          "epc_rating": {
            "type": "string",
            "description": "Optional. EPC rating label.",
            "example": "C"
          },
          "epc_current_rating": {
            "type": "string",
            "description": "Optional. EPC current rating.",
            "example": "C"
          },
          "epc_potential_rating": {
            "type": "string",
            "description": "Optional. EPC potential rating.",
            "example": "B"
          },
          "parking": {
            "type": "string",
            "description": "Optional. Parking description or availability flag.",
            "example": "Driveway"
          },
          "garage": {
            "type": "string",
            "description": "Optional. Garage description or availability flag.",
            "example": "Single garage"
          },
          "garden": {
            "type": "string",
            "description": "Optional. Garden description or availability flag.",
            "example": "Rear garden"
          },
          "balcony": {
            "type": "string",
            "description": "Optional. Balcony description or availability flag.",
            "example": "Private balcony"
          },
          "lift": {
            "type": "string",
            "description": "Optional. Lift access description or availability flag.",
            "example": "Lift access"
          },
          "accessibility": {
            "type": "string",
            "description": "Optional. Accessibility notes.",
            "example": "Step-free entrance"
          },
          "let_min_term": {
            "type": "string",
            "description": "Optional. Lettings minimum term.",
            "example": "12+ months"
          },
          "let_type_id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. 1 Long term, 2 Short term, 3 Student, or matching label.",
            "example": 1
          },
          "let_furn_id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. 1 Unfurnished, 2 Furnished, 3 Part Furnished, or matching label.",
            "example": 2
          },
          "size_min": {
            "type": "number",
            "description": "Optional. Minimum floor/land area for commercial or land.",
            "example": 1800
          },
          "size_max": {
            "type": "number",
            "description": "Optional. Maximum floor/land area for commercial or land.",
            "example": 1800
          },
          "area_size_unit_id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional. Area unit. 1/sqft/sq ft/square feet or 2/sqm/sq m/square metres.",
            "example": "sqft"
          },
          "price_per_unit": {
            "type": "number",
            "description": "Optional. Commercial or land price per area unit.",
            "example": 18
          },
          "floor_area": {
            "type": "number",
            "description": "Optional. Floor area value.",
            "example": 1200
          },
          "floor_area_unit": {
            "type": "string",
            "description": "Optional. Floor area unit.",
            "example": "sqft"
          },
          "plot_area": {
            "type": "number",
            "description": "Optional. Plot or land area value.",
            "example": 0.25
          },
          "plot_area_unit": {
            "type": "string",
            "description": "Optional. Plot area unit.",
            "example": "acres"
          },
          "summary": {
            "type": "string",
            "description": "Optional. Short description.",
            "example": "Three-bedroom house near local amenities."
          },
          "description": {
            "type": "string",
            "description": "Optional. Full property description.",
            "example": "A well-presented three-bedroom property with garden and off-street parking."
          },
          "features": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "Optional. Pipe-delimited, double-pipe-delimited or array of feature strings.",
            "example": [
              "Garden",
              "Driveway",
              "Close to station"
            ]
          },
          "image_0": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Legacy canonical image slot 0. Prefer media_image_00.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/front.jpg"
          },
          "image_1": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Legacy canonical image slot 1. image_0 through image_10 are supported."
          },
          "image_caption_0": {
            "type": "string",
            "description": "Optional. Caption for image_0. image_caption_0 through image_caption_10 are supported.",
            "example": "Front elevation"
          },
          "media_image_00": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Primary media image. media_image_00 through media_image_69 are supported.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/front.jpg"
          },
          "media_image_text_00": {
            "type": "string",
            "description": "Optional. Caption/label for media_image_00. media_image_text_00 through media_image_text_69 are supported.",
            "example": "Front elevation"
          },
          "media_floorplan_00": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Floorplan media. media_floorplan_00 through media_floorplan_19 are supported.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/floorplan.jpg"
          },
          "floorplan": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Legacy canonical floorplan field. Prefer media_floorplan_00.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/floorplan.jpg"
          },
          "epc_image": {
            "type": "string",
            "format": "uri",
            "description": "Optional. EPC image URL or filename.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/epc.jpg"
          },
          "epc_pdf": {
            "type": "string",
            "format": "uri",
            "description": "Optional. EPC PDF document URL or filename.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/epc.pdf"
          },
          "brochure_pdf": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Brochure PDF document URL or filename.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/brochure.pdf"
          },
          "media_document_00": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Generic document slot. media_document_00 through media_document_19 are supported.",
            "example": "https://cdn.example.com/properties/CRM-SALE-001/brochure.pdf"
          },
          "media_document_text_00": {
            "type": "string",
            "description": "Optional. Document label for media_document_00. media_document_text_00 through media_document_text_19 are supported.",
            "example": "Brochure"
          },
          "virtual_tour": {
            "type": "string",
            "format": "uri",
            "description": "Optional legacy virtual tour alias. Prefer virtual_tour_url.",
            "example": "https://tours.example.com/CRM-SALE-001"
          },
          "virtual_tour_url": {
            "type": "string",
            "format": "uri",
            "description": "Optional. HTTPS virtual tour URL. Matterport, EyeSpy360, YouTube, Vimeo, Kuula, configured Generic HTTPS domains and securely streamed uploaded MP4 tours are supported.",
            "example": "https://my.matterport.com/show/?m=abc123"
          },
          "virtual_tour_provider": {
            "type": "string",
            "description": "Optional. Detected automatically from virtual_tour_url.",
            "enum": [
              "Matterport",
              "EyeSpy360",
              "YouTube",
              "Vimeo",
              "Kuula",
              "Generic",
              "Uploaded Video"
            ],
            "example": "Matterport"
          },
          "virtual_tour_type": {
            "type": "string",
            "enum": [
              "iframe",
              "uploaded_video"
            ],
            "readOnly": true,
            "description": "Runtime rendering type for the public modal."
          },
          "virtual_tour_enabled": {
            "type": "boolean",
            "description": "Whether the virtual tour should be displayed publicly.",
            "example": true
          },
          "video": {
            "type": "string",
            "format": "uri",
            "description": "Optional. Video URL.",
            "example": "https://video.example.com/CRM-SALE-001"
          },
          "source_type": {
            "type": "string",
            "readOnly": true,
            "description": "Optional ownership metadata. Normally supplied by the authenticated connection, not clients.",
            "example": "GENERIC_CRM"
          },
          "feed_connection_id": {
            "type": "integer",
            "readOnly": true,
            "description": "Optional ownership metadata. Normally derived from authentication.",
            "example": 123
          },
          "external_property_id": {
            "type": "string",
            "readOnly": true,
            "description": "Optional ownership metadata mirroring external_ref when stored internally.",
            "example": "CRM-SALE-001"
          },
          "managed_mode": {
            "type": "string",
            "readOnly": true,
            "description": "Optional ownership mode metadata.",
            "enum": [
              "CRM_MASTER",
              "HYBRID",
              "PORTAL_MASTER"
            ],
            "example": "CRM_MASTER"
          },
          "last_sync_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "Optional sync metadata timestamp."
          },
          "sync_status": {
            "type": "string",
            "readOnly": true,
            "description": "Optional sync metadata status.",
            "example": "OK"
          },
          "sync_error": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Optional sync metadata error."
          },
          "seen_in_run_id": {
            "type": "integer",
            "nullable": true,
            "readOnly": true,
            "description": "Optional feed run visibility marker."
          },
          "owner_id": {
            "type": "string",
            "readOnly": true,
            "description": "Optional ownership metadata."
          },
          "owner_type": {
            "type": "string",
            "readOnly": true,
            "description": "Optional ownership metadata."
          },
          "owner_name": {
            "type": "string",
            "readOnly": true,
            "description": "Optional ownership metadata."
          },
          "field_owner": {
            "type": "string",
            "readOnly": true,
            "description": "Optional field ownership metadata."
          },
          "field_owner_map": {
            "oneOf": [
              {
                "type": "object"
              },
              {
                "type": "string"
              }
            ],
            "readOnly": true,
            "description": "Optional per-field ownership metadata map."
          },
          "ownership_source": {
            "type": "string",
            "readOnly": true,
            "description": "Optional field ownership source."
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "Optional last seen timestamp for sync visibility."
          },
          "source_updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "Optional source updated timestamp supplied by the provider."
          },
          "uprn": {
            "type": "string",
            "description": "Optional Unique Property Reference Number."
          },
          "what3words": {
            "type": "string",
            "description": "Optional What3Words address."
          },
          "portal_url": {
            "type": "string",
            "format": "uri",
            "description": "Optional source portal URL."
          },
          "seo_title": {
            "type": "string",
            "description": "Optional SEO title."
          },
          "seo_description": {
            "type": "string",
            "description": "Optional SEO description."
          },
          "floor_level": {
            "type": "string",
            "description": "Optional floor level."
          },
          "rooms": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              {
                "type": "object"
              },
              {
                "type": "string"
              }
            ],
            "description": "Optional room data supplied by a CRM/feed."
          },
          "flood_risk": {
            "type": "string",
            "description": "Optional flood-risk note."
          },
          "material_information": {
            "type": "string",
            "description": "Optional material information summary."
          }
        },
        "x-canonical-ranges": {
          "image": "image_0 through image_10",
          "image_caption": "image_caption_0 through image_caption_10",
          "media_image": "media_image_00 through media_image_69",
          "media_image_text": "media_image_text_00 through media_image_text_69",
          "media_floorplan": "media_floorplan_00 through media_floorplan_19",
          "media_floorplan_text": "media_floorplan_text_00 through media_floorplan_text_19",
          "media_document": "media_document_00 through media_document_19",
          "media_document_text": "media_document_text_00 through media_document_text_19"
        }
      },
      "SuccessResponse": {
        "type": "object",
        "required": [
          "success",
          "request_id",
          "external_ref",
          "status",
          "message"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "request_id": {
            "type": "string"
          },
          "external_ref": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "updated",
              "withdrawn",
              "deleted",
              "archived",
              "restored",
              "reactivated",
              "completed"
            ]
          },
          "message": {
            "type": "string"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            }
          }
        }
      },
      "SandboxPropertyResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SuccessResponse"
          },
          {
            "type": "object",
            "properties": {
              "property": {
                "$ref": "#/components/schemas/Property"
              }
            }
          }
        ]
      },
      "SandboxPropertyListResponse": {
        "type": "object",
        "required": [
          "success",
          "data",
          "pagination"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Property"
            }
          },
          "pagination": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer"
              },
              "offset": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              },
              "next_offset": {
                "type": "integer",
                "nullable": true
              }
            }
          }
        }
      },
      "SandboxWebhookListResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "event_type": {
                  "type": "string",
                  "enum": [
                    "property.created",
                    "property.updated",
                    "property.withdrawn",
                    "property.archived",
                    "property.restored",
                    "property.reactivated",
                    "property.deleted"
                  ]
                },
                "external_ref": {
                  "type": "string"
                },
                "delivery_status": {
                  "type": "string"
                },
                "attempts": {
                  "type": "integer"
                },
                "payload_json": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "$ref": "#/components/schemas/Error"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message",
          "details",
          "request_id",
          "retryable"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "unauthorized",
              "forbidden",
              "not_found",
              "method_not_allowed",
              "validation_failed",
              "duplicate_request",
              "already_processing",
              "rate_limited",
              "payload_too_large",
              "conflict",
              "internal_error"
            ]
          },
          "message": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            }
          },
          "request_id": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean"
          }
        }
      },
      "ErrorDetail": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "api_version": {
            "type": "string"
          },
          "service_status": {
            "type": "string"
          },
          "authentication_enabled": {
            "type": "boolean"
          },
          "current_time": {
            "type": "string",
            "format": "date-time"
          },
          "supported_versions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CapabilitiesResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "api_version": {
            "type": "string"
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": {
              "type": "boolean"
            }
          },
          "enums": {
            "type": "object"
          },
          "external_ref": {
            "type": "object"
          }
        }
      },
      "VersionResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "api_version": {
            "type": "string"
          },
          "build_version": {
            "type": "string"
          },
          "supported_features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "deprecation_policy": {
            "type": "object"
          }
        }
      }
    }
  }
}
