İçeriğe geç

Быстрый старт интеграции

Bu içerik henüz dilinizde mevcut değil.

Интеграционный API живёт под /api/v1/integration/*, версионирован и не меняется несовместимо в рамках v1. Полный справочник — раздел API интегратора слева; он собран из той же спецификации, по которой работает сама система.

Администратор компании создаёт ключ на экране Управление доступом → API-ключи: имя, скоупы, лимит запросов в минуту (по умолчанию 2000). Значение ключа показывается один раз.

Скоупы:

Скоуп Даёт
orders:write создание и изменение заявок, отмена, отказ, переупаковка, оплата, мини-статусы, теги
orders:read pull заявок со статусами
trips:read pull рейсов
vehicles:read чтение машин
couriers:write синхронизация водителей

Ключ передаётся в каждом запросе:

Authorization: ApiKey <ключ>

Проверка, что ключ живой и к какой компании относится:

GET /api/v1/integration/whoami
→ { "tenantId": "722d70cb-…", "keyId": "7d95dc4b-…", "scopes": ["orders:write"] }

POST /api/v1/integration/orders/sync — upsert по externalId: повторная отправка с тем же externalId обновляет заявку, а не создаёт вторую. Это и есть идемпотентность интеграции: безопасно ретраить и досылать.

{
"externalId": "ERP-000123",
"customId": null,
"type": "Delivery",
"price": 1250.0,
"deliveryPrice": 0,
"planDeliveryPeriod": { "startDate": "2026-09-03T09:00:00Z", "endDate": "2026-09-03T13:00:00Z" },
"planPickupPeriod": null,
"addressFrom": null,
"addressTo": {
"line": "Bakı, Nizami küç. 10",
"lat": 40.3777,
"long": 49.8531,
"details": null, "commentary": "звонить за 15 минут", "domofon": null, "flat": "12", "floor": "3", "porch": "1"
},
"appType": null,
"volume": 0.12,
"weight": 18.5,
"priority": 0,
"additionalDetails": null,
"details": null,
"assembled": true,
"statusGroup": null,
"relatedToOrderId": null,
"assignedDriverId": null,
"assignedVehicleId": null,
"lines": [
{ "code": "SKU-1", "name": "Кофе 1 кг", "productId": "…", "warehouseId": "…", "requestedQty": 10,
"weight": 1.0, "volume": 0.002, "dimensions": null, "requiredSkills": null, "priority": null, "unitPrice": 12.5, "role": null }
],
"client": { "…": "…" }
}

Ответ 201 (создано) или 200 (обновлено):

{ "orderId": "…", "shortId": "A7K2Q", "outcome": "Created", "type": "Delivery" }

Пакетная отправка — POST /api/v1/integration/orders/sync/bulk; ответ содержит результат по каждому элементу и ошибки по индексу, одна плохая заявка не роняет пакет.

Что проверяется на входе (иначе 400 с полем и кодом): у каждой строки requestedQty > 0 (orders.line.requestedQty.invalid), вес и объём не отрицательные (orders.line.measure.invalid), productId и warehouseId существуют в вашей компании и не архивированы (orders.line.product.notFound, orders.line.warehouse.notFound, поле lines[i].productId / lines[i].warehouseId), хотя бы у одного адреса есть координаты (orders.coordinates.required).

Водитель и машина из ERP. Если в вашей системе заявка уже закреплена за конкретным водителем и машиной, передайте оба идентификатора — assignedDriverId и assignedVehicleId (GUID из справочников PickUpper: GET /api/drivers?externalId=… находит водителя по вашему коду, GET /api/vehicles?search=… — машину по названию или номеру). Планирование примет пару как данность: заявка не будет отдана другому водителю, автоплан только построит маршрут по её стопам. Правила: оба поля или ни одного (orders.assignment.incomplete), водитель существует и не в архиве (orders.assignment.driverNotFound), машина активна (orders.assignment.vehicleNotFound). Если к моменту планирования водитель или машина попали в архив, заявка останется незапланированной с причиной planning.assigned_pair_unavailable — пришлите новую пару повторным sync (null в повторной отправке пару не снимает).

Что обязательно, чтобы заявка попала в план: координаты адреса, окно доставки, вес и объём по строкам. Заявка без габаритов не считается «нулевой» — планировщик откажет с кодом причины.

Повторная отправка и строки. Пока заявка не поставлена на рейс (статусы New, InPlanning) и по её строкам нет исходов, повторный sync обновляет строки: строка с тем же code сохраняет свой идентификатор и меняет количество/вес/объём, новый code добавляется, отсутствующий убирается. Как только заявка спланирована или по ней записан исход, изменённая строка отвечает 409 orders.lines.immutable — правка не теряется молча, ERP узнаёт, что её нужно сделать через отмену или возврат в интейк. Повторная отправка отменённой заявки возвращает её в работу: ответ 200, outcome: "Restored", статус New.

Pull с курсором, не опрос всей базы:

GET /api/v1/integration/orders?since=2026-09-03T00:00:00Z&limit=200
→ { "items": [ … ], "nextCursor": "eyJ…" }
GET /api/v1/integration/orders?cursor=eyJ…

Курсор непрозрачный; храните последний nextCursor и продолжайте с него — страницы упорядочены по времени изменения и не теряют и не дублируют строки на границе. null в nextCursor — вы догнали ленту. То же для рейсов: GET /api/v1/integration/trips.

Статусы заявки: New → InPlanning → Planned → InProgress → FullyDelivered | PartiallyDelivered | Returned | Collected | PartiallyCollected | NotCollected | Refused | Cancelled → Closed.

Заявка на забор (Pickup) и возврат (Return) завершаются статусами Collected (забрано всё), PartiallyCollected (забрана часть) или NotCollected (ничего не забрано) — там, где доставка получает FullyDelivered, PartiallyDelivered или Returned. Обмен (Rebox) получает статусы доставки и становится FullyDelivered, только когда новое передано и старое забрано. Список статусов может расти: неизвестное значение считайте незавершённым, а не ошибкой.

  • Любая ошибка — Problem Details с полем type из каталога кодов; ошибки валидации несут errors по полям.
  • Лимит запросов — на ключ, в минуту. Превышение: 429, type: orders.rate_limited, заголовок Retry-After.
  • Один ключ — одна компания: данные другой компании через него недоступны на уровне базы.