# Integration Guide

This guide describes the normal CRM property lifecycle.


## Integration Methods

A. **File Upload**: BLM, XML and CSV upload is for manual or one-time imports.

B. **Feed URL**: URL_PULL connections store a public feed URL. Cron downloads BLM/XML/CSV automatically, queues normal Feed Engine processing and reschedules by the configured poll interval, defaulting to 60 minutes. Missing listings can be inactivated for URL feeds unless configured as `DO_NOTHING`.

C. **Quardlyn Developer API**: preferred near-real-time CRM integration. The CRM pushes signed canonical property changes directly to Quardlyn.

Outbound Quardlyn-to-CRM webhooks are Beta / Sandbox Simulation Only and are not part of the production launch integration.

## 1. Check API Availability

```text
GET /api/v1/crm/health
GET /api/v1/crm/capabilities
GET /api/v1/crm/version
```

## 2. Create Property

```text
POST /api/v1/crm/properties
```

Send a canonical property payload with `property.external_ref`.

## 3. Update Property

```text
PUT /api/v1/crm/properties/{external_ref}
PATCH /api/v1/crm/properties/{external_ref}
```

Use the same `external_ref`. The path value wins if it differs from the body.

## 3.1 Production Reads

The v1 Production GET API is available. Use it to read properties owned by the authenticated CRM connection.

Live read endpoints include:

```text
GET /api/v1/crm/properties
GET /api/v1/crm/properties/{external_ref}
GET /api/v1/crm/properties/lookup?id_type=external_ref&value={external_ref}
GET /api/v1/crm/properties/lookup?id_type=property_uid&value={property_uid}
```

Single-property reads return the full canonical payload by default. Collection reads return summary payloads by default and use `expand=full` when a client genuinely needs full canonical objects for every result.

Collection reads support `limit`, `offset`, `status`, `marketing_status`, `department`, `published`, `include_archived`, `updated_since`, `created_since`, `sort`, `order`, `fields` and `expand`.

Examples:

```text
GET /api/v1/crm/properties?limit=25&offset=50
GET /api/v1/crm/properties?marketing_status=available&department=sale
GET /api/v1/crm/properties?fields=property_uid,external_ref,title,price,status
GET /api/v1/crm/properties?expand=full
```

GET responses include `ETag`. Send `If-None-Match` with a previous ETag to receive `304 Not Modified` when the representation has not changed.

Reserved future read-discovery endpoints are documented in `production-get-api-contract.md`, but are not public runtime endpoints yet.

## 4. Media

Send media using canonical feed slots such as `property.media_image_00`, `property.media_image_01`, `property.media_floorplan_00`, `property.epc_image` and `property.virtual_tour_url`. The legacy `property.virtual_tour` alias is still accepted.

```json
{
  "type": "image",
  "url": "https://cdn.example.com/property/image.jpg",
  "caption": "Front elevation",
  "order": 1
}
```

Supported media types:

- `image`
- `floorplan`
- `epc_image`
- `virtual_tour`
- `virtual_tour_url`

## 5. Documents

Send documents using canonical fields such as `property.brochure_pdf`, `property.epc_pdf` and `property.media_document_00`.

```json
{
  "type": "brochure",
  "url": "https://cdn.example.com/property/brochure.pdf",
  "name": "Brochure"
}
```

Supported document types:

- `brochure`
- `epc`
- `floorplan`
- `other`

## 6. Withdraw

```text
POST /api/v1/crm/properties/{external_ref}/withdraw
```

## 7. Archive

```text
POST /api/v1/crm/properties/{external_ref}/archive
```

## 8. Restore

```text
POST /api/v1/crm/properties/{external_ref}/restore
```

## 9. Delete

```text
DELETE /api/v1/crm/properties/{external_ref}
```

## Idempotency

Use a stable idempotency key per CRM event. Reuse the same key for retries of the same event.

## External Reference

`property.external_ref` is case-sensitive and unique per authenticated connection. It must be URL-encoded when used in a path segment.
