{
    "openapi": "3.0.3",
    "info": {
        "title": "Quardlyn CRM API",
        "version": "v1"
    },
    "servers": [
        {
            "url": "/api/v1/crm"
        }
    ],
    "paths": {
        "/api/v1/crm/health": {
            "get": {
                "summary": "Read CRM API health"
            }
        },
        "/api/v1/crm/capabilities": {
            "get": {
                "summary": "Read CRM API capabilities"
            }
        },
        "/api/v1/crm/version": {
            "get": {
                "summary": "Read CRM API version"
            }
        },
        "/api/v1/crm/schema": {
            "get": {
                "summary": "Read CRM API schema"
            }
        },
        "/api/v1/crm/openapi": {
            "get": {
                "summary": "Read CRM API OpenAPI contract"
            }
        },
        "/api/v1/crm/properties": {
            "get": {
                "summary": "List properties visible to this integration"
            },
            "post": {
                "summary": "Create or upsert a CRM property event"
            }
        },
        "/api/v1/crm/properties/lookup": {
            "get": {
                "summary": "Lookup a CRM property by source identity"
            }
        },
        "/api/v1/crm/properties/{externalRef}": {
            "get": {
                "summary": "Read a CRM property by external reference"
            },
            "put": {
                "summary": "Replace a CRM property payload"
            },
            "patch": {
                "summary": "Update a CRM property payload"
            },
            "delete": {
                "summary": "Delete, withdraw, or inactivate a CRM property"
            }
        },
        "/api/v1/crm/properties/{externalRef}/withdraw": {
            "post": {
                "summary": "Withdraw a CRM property"
            }
        },
        "/api/v1/crm/properties/{externalRef}/archive": {
            "post": {
                "summary": "Archive a CRM property"
            }
        },
        "/api/v1/crm/properties/{externalRef}/restore": {
            "post": {
                "summary": "Restore an archived property"
            }
        },
        "/api/v1/crm/properties/{externalRef}/reactivate": {
            "post": {
                "summary": "Reactivate a previously inactive property"
            }
        },
        "/properties": {
            "get": "List properties",
            "post": "Create property"
        },
        "/properties/{external_ref}": {
            "get": "Get property",
            "put": "Replace/update property",
            "patch": "Partial update property",
            "delete": "Delete/withdraw property"
        }
    },
    "components": {
        "schemas": {
            "CrmEventEnvelope": {
                "required": [
                    "event_id",
                    "event_type",
                    "event_version",
                    "occurred_at",
                    "property_ref",
                    "payload"
                ],
                "fields": {
                    "event_id": "Stable unique event id from the CRM. Used for idempotency/replay.",
                    "event_type": "property.created/property.updated/property.withdrawn/property.deleted/property.archived/property.restored/property.reactivated",
                    "event_version": "Envelope version. Use v1.",
                    "occurred_at": "ISO-8601 UTC timestamp when the source event occurred.",
                    "branch_ref": "Provider branch/office reference when available.",
                    "property_ref": "Provider property reference. Also becomes property.external_ref if payload omits it.",
                    "sequence": "Monotonically increasing per-property source sequence, preferred for ordering.",
                    "source_version": "Alias for sequence when the CRM uses source versions.",
                    "source_updated_at": "Fallback ordering timestamp when no sequence is available.",
                    "payload": "Canonical CRM property object, not a second custom schema."
                },
                "legacy_body": "Existing non-envelope {property:{...}} requests remain accepted, but certified integrations should use the envelope."
            },
            "CanonicalProperty": {
                "schema": "Quardlyn canonical import property model",
                "required_for_ingestion": [
                    "external_ref",
                    "department",
                    "display_address"
                ],
                "required_for_live_publish": [
                    "external_ref",
                    "department",
                    "postcode",
                    "price or price_display"
                ],
                "field_groups": {
                    "identity_control": [
                        "external_ref",
                        "department",
                        "marketing_status",
                        "published",
                        "status_id",
                        "trans_type_id"
                    ],
                    "address_coordinates": [
                        "display_address",
                        "address_1",
                        "address_2",
                        "address_3",
                        "address_4",
                        "town",
                        "county",
                        "country",
                        "postcode",
                        "postcode1",
                        "postcode2",
                        "latitude",
                        "longitude",
                        "lat",
                        "lng"
                    ],
                    "property_details": [
                        "property_type",
                        "property_subtype",
                        "beds",
                        "baths",
                        "receptions",
                        "new_home_flag"
                    ],
                    "pricing_sale_auction_commercial": [
                        "price",
                        "price_display",
                        "price_qualifier",
                        "tenure",
                        "tenure_type_id",
                        "size_min",
                        "size_max",
                        "area_size_unit_id",
                        "price_per_unit"
                    ],
                    "lettings": [
                        "rent_frequency",
                        "deposit",
                        "available_from",
                        "let_min_term",
                        "let_type_id",
                        "let_furn_id",
                        "is_student_accommodation",
                        "is_house_share",
                        "pets_policy",
                        "bills_included"
                    ],
                    "content_features": [
                        "summary",
                        "description",
                        "features"
                    ],
                    "media_documents": [
                        "media_image_00..69",
                        "image_0..10",
                        "image_caption_0..10",
                        "media_floorplan_00..19",
                        "floorplan",
                        "epc_image",
                        "epc_pdf",
                        "brochure_pdf",
                        "media_document_00..19",
                        "virtual_tour"
                    ],
                    "ownership_future_ready": [
                        "source_type",
                        "feed_connection_id",
                        "external_property_id",
                        "managed_mode",
                        "last_sync_at",
                        "sync_status",
                        "sync_error",
                        "seen_in_run_id"
                    ]
                }
            },
            "ErrorResponse": {
                "shape": {
                    "success": false,
                    "error": [
                        "code",
                        "message",
                        "details",
                        "request_id",
                        "support_reference",
                        "retryable"
                    ]
                },
                "codes": [
                    "unauthorized",
                    "forbidden",
                    "not_found",
                    "method_not_allowed",
                    "validation_failed",
                    "duplicate_request",
                    "already_processing",
                    "rate_limited",
                    "payload_too_large",
                    "conflict",
                    "internal_error"
                ],
                "safe_messages_only": true,
                "no_internal_diagnostics": [
                    "tokens",
                    "passwords",
                    "raw signatures",
                    "stack traces",
                    "SQL",
                    "filesystem paths"
                ]
            }
        }
    }
}
