Концепции

Upsert паттерн

Один endpoint для создания и обновления — без проверки существования

API использует upsert-паттерн: один PUT запрос создаёт новый объект или обновляет существующий. Вам не нужно проверять, есть ли объект в системе — API делает это автоматически.

Зачем это нужно

В классическом REST API для синхронизации данных нужно:

1. GET /listings/external/APT-001     # Проверить существование
2. Если 404 → POST /listings          # Создать
   Если 200 → PUT /listings/42        # Обновить

С upsert-паттерном всё проще:

1. PUT /listings { externalId: "APT-001", ... }
   # API сам создаст или обновит

Как работает

СитуацияЧто происходит
Объект не найденСоздаётся новый
Объект найденОбновляется существующий

Поиск происходит по id или externalId — что передали в запросе.

Пример: создание

Первый запрос с externalId: "APT-001" создаёт объявление:

curl -X PUT https://crm.rentix.md/api/v1/listings \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "APT-001",
    "announcementType": "rent",
    "propertyType": "residential",
    "propertySecondaryType": "apartment",
    "announcementValue": 500,
    "announcementCurrency": "EUR"
  }'
Ответ
{
  "id": 42,
  "externalId": "APT-001",
  "status": "draft",
  "created": true,
  "updated": false
}

Обратите внимание: created: true — объект создан.

Пример: обновление

Повторный запрос с тем же externalId обновляет объявление:

curl -X PUT https://crm.rentix.md/api/v1/listings \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "APT-001",
    "announcementValue": 600
  }'
Ответ
{
  "id": 42,
  "externalId": "APT-001",
  "status": "draft",
  "created": false,
  "updated": true
}

Теперь updated: true — объект обновлён.

Частичное обновление

При обновлении передавайте только изменённые поля. Остальные сохранят свои значения:

// Первый запрос — полное создание
{
  "externalId": "APT-001",
  "announcementType": "rent",
  "announcementValue": 500,
  "propertyArea": 65,
  "propertyFloorNumber": 3
}

// Второй запрос — меняем только цену
{
  "externalId": "APT-001",
  "announcementValue": 600
}
// propertyArea и propertyFloorNumber останутся прежними

Очистка полей

Чтобы очистить поле (удалить значение), отправьте null:

{
  "externalId": "APT-001",
  "propertyFloorNumber": null
}

После этого запроса propertyFloorNumber будет пустым.

Преимущества

Простота интеграции

Один endpoint на все случаи. Не нужно писать логику проверки существования.

Идемпотентность

Повторный запрос с теми же данными не создаст дубликат — просто обновит существующий объект. Это важно при сбоях сети: можно безопасно повторить запрос.

Атомарность

Создание или обновление происходит в одной транзакции. Не бывает ситуации «проверил — не было, создал — дубликат».

Bulk upsert

Тот же принцип работает для массовых операций. До 100 объектов за запрос. Каждая операция должна содержать поле op:

curl -X POST https://crm.rentix.md/api/v1/listings/bulk \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "upsert", "externalId": "APT-001", "announcementValue": 500 },
      { "op": "upsert", "externalId": "APT-002", "announcementValue": 700 },
      { "op": "upsert", "externalId": "APT-003", "announcementValue": 450 }
    ]
  }'
Ответ
{
  "results": [
    { "op": "upsert", "externalId": "APT-001", "id": 42, "success": true, "created": false, "updated": true },
    { "op": "upsert", "externalId": "APT-002", "id": 43, "success": true, "created": true, "updated": false },
    { "op": "upsert", "externalId": "APT-003", "id": 44, "success": true, "created": true, "updated": false }
  ],
  "summary": { "total": 3, "succeeded": 3, "failed": 0 }
}

Ограничения

Нельзя передать оба id и externalId одновременно — будет ошибка. Выберите один способ идентификации.
РесурсEndpoint
ОбъявленияPUT /listings
ПользователиPUT /users
Copyright © 2026