для разработчиков · REST API

API Logtor

Подключите интернет-магазин к доставке: расчёт тарифов, пункты выдачи, подсказки городов и адресов, создание заказа, статусы и вебхуки. REST/JSON, авторизация по API-ключу. Сейчас работают СДЭК и DPD (пункт → пункт); Достависта (курьер по адресам) временно скрыта, КСЭ — в подключении; Деловые Линии и ПЭК — в планах. Новые перевозчики подключаются без изменения контракта.

Базовый URL: https://logtor.ru/api/v1/public
00

Быстрый старт

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

# 1. посчитать варианты (габариты нужны, иначе DPD не посчитается)
curl -s https://logtor.ru/api/v1/public/calculate   -H "X-Api-Key: $KEY" -H "Content-Type: application/json"   -d '{"sender_city":"Москва","receiver_city":"Казань","weight":1,
       "length":20,"width":15,"height":10}'

# 2. взять пункт приёма и пункт выдачи
curl -s "https://logtor.ru/api/v1/public/pickup-points?city=Москва&purpose=shipment" -H "X-Api-Key: $KEY"
curl -s "https://logtor.ru/api/v1/public/pickup-points?city=Казань&purpose=delivery" -H "X-Api-Key: $KEY"

# 3. создать заказ (Idempotency-Key обязателен)
curl -s https://logtor.ru/api/v1/public/orders   -H "X-Api-Key: $KEY" -H "Idempotency-Key: shop-1001" -H "Content-Type: application/json"   -d '{"tc_id":1,"tariff_code":136,
       "sender_name":"Иванов Иван","sender_phone":"+79990001122",
       "sender_city":"Москва","sender_address":"Тверская 1","sender_pvz":"MSK2567",
       "receiver_name":"Петров Пётр","receiver_phone":"+79991234567",
       "receiver_city":"Казань","receiver_address":"Баумана 1","receiver_pvz":"KZN3",
       "weight":1,"length":20,"width":15,"height":10}'

# 4. этикетка (PDF)
curl -s https://logtor.ru/api/v1/public/orders/{uuid}/label -H "X-Api-Key: $KEY" -o label.pdf
  • галками отмечено обязательное: Idempotency-Key при создании и габариты в расчёте
  • сейчас работают только тарифы пункт → пункт: посылку сдают в пункт приёма, получатель забирает в пункте выдачи
  • статусы лучше получать вебхуком, чем опросом — см. раздел 05
00

Авторизация

Каждому магазину выдаётся свой API-ключ (ms_live_…). Он показывается один раз — при создании или перевыпуске в кабинете; у нас хранится только отпечаток, поэтому потерянный ключ не восстановить, а только перевыпустить. Передавайте ключ в заголовке запроса:

X-Api-Key: <ваш_ключ>

Если ключ неверный или не указан — сервер ответит HTTP 401. Если аккаунт ещё не подтверждён оператором — тоже 401: ключи начинают работать после подтверждения.

00.1

Защита от дублей (идемпотентность)

При создании заказа обязательно передавайте заголовок Idempotency-Key — уникальный идентификатор вашей заявки. Один и тот же ключ = один заказ, повторный запрос не создаст дубликат ни в Logtor, ни у перевозчика.

Idempotency-Key: <уникальный_идентификатор_заявки>
01

Расчёт тарифов

POST/calculate

Тело запроса:

{
  "sender_city": "Москва",
  "receiver_city": "Казань",
  "weight": 1.2,
  "length": 15,
  "width": 15,
  "height": 15,
  "declared_value": 300
}

Габариты передавайте всегда. Без длины, ширины и высоты тариф DPD не возвращается: его расчёт без габаритов не учитывает объёмный вес и занижает цену (Москва → Сочи, 1 кг: 234 ₽ без габаритов против 308 ₽ с 40×30×20). Лучше честно не показать тариф, чем назвать цену, которую перевозчик потом пересчитает.

Ответ — массив тарифов с итоговой ценой (уже с учётом наценки):

[
  {
    "tc_id": 1,
    "tc_name": "СДЭК",
    "tariff_code": 136,
    "tariff_name": "Посылка · ПВЗ → ПВЗ",
    "cost": 345.0,
    "delivery_days_min": 1,
    "delivery_days_max": 4,
    "requires_pickup_point": true
  }
]
  • tariff_code — код тарифа (передаётся в заказ)
  • requires_pickup_point: true — для этого тарифа нужен пункт выдачи (receiver_pvz)
  • cost — итоговая цена, фиксируется в заказе
02

Пункты выдачи и приёма

GET/pickup-points?city=Москва&purpose=delivery

Передайте заголовок X-Api-Key — вернутся пункты тех перевозчиков, которые подключены вашей организации (без ключа — только для формы на лендинге). Чтобы получить пункты одного перевозчика, добавьте tc_id из ответа расчёта: /pickup-points?city=Казань&tc_id=7.

purpose=delivery — пункты выдачи для получателя (ПВЗ), purpose=shipment — пункты приёма, куда сдать отправление (склад перевозчика).

[
  {
    "id": "KZN3",
    "name": "KZN3, Казань, ул. Спартаковская",
    "address": "ул. Спартаковская, 2, корп.1, 112",
    "city": "Казань",
    "work_hours": "Пн-Пт 09:00-20:00"
  }
]

Поле id — код пункта, который нужно передавать в заказ в sender_pvz / receiver_pvz.

03

Создание заказа

POST/orders

Заголовки:

X-Api-Key: <ключ>
Idempotency-Key: <уникальный_идентификатор_заявки>

Тело:

{
  "tc_id": 1,
  "tariff_code": 136,
  "sender_name": "Иванов Иван",
  "sender_phone": "+79990001122",
  "sender_city": "Москва",
  "sender_pvz": "MSK2567",
  "receiver_name": "Петров Пётр",
  "receiver_phone": "+79991234567",
  "receiver_city": "Казань",
  "receiver_pvz": "KZN3",
  "weight": 1.2,
  "length": 15,
  "width": 15,
  "height": 15,
  "declared_value": 300,
  "callback_url": "https://your-site.ru/logtor/webhook"
}
  • для тарифов «от станции/склада» обязателен sender_pvz
  • receiver_pvz обязателен, если тариф требует пункт выдачи
  • цена не передаётся — сервер вычисляет и проверяет её сам (защита от подмены)
  • callback_url — сюда придут уведомления о статусах

Сейчас доступны ПВЗ-тарифы «склад → склад»: отправитель сдаёт посылку в пункт приёма выбранного города, получатель забирает в пункте выдачи. Курьерские тарифы «от двери» пока не предлагаются — как только появятся, для них потребуется дата забора intake_date (в API уже поддерживается).

Ответ:

{
  "id": 42,
  "uuid": "…",
  "internal_number": "MS-0004-00001",
  "tc_name": "СДЭК",
  "tc_tracking_number": "…",
  "client_cost": 345.0,
  "status": "confirmed",
  "created_at": "…"
}
04

Статусы / трекинг

GET/tracking/{tracking_number}

tracking_number — номер из поля tc_tracking_number.

{
  "order": {
    "id": 42,
    "tc_tracking_number": "…",
    "status": "in_transit",
    "client_cost": 345.0
  },
  "tracking": [
    { "status": "created", "description": "Заказ создан", "event_time": "…" },
    { "status": "in_transit", "description": "В пути", "location": "Москва", "event_time": "…" }
  ]
}
05

Уведомления о статусах (callback)

На callback_url при изменении статуса приходит POST
{
  "event": "status_changed",
  "event_id": "…",
  "order_uuid": "…",
  "internal_number": "MS-0004-00001",
  "tc_tracking_number": "…",
  "status": "in_transit",
  "updated_at": "…"
}

Заголовки подписи (защита от подделки запросов):

X-Logtor-Signature: sha256=HMAC(секрет_подписи_магазина, "{timestamp}.{raw_body}")
X-Logtor-Event-Id: <uuid события>
X-Logtor-Timestamp: <ts>

Проверка: вычислите HMAC-SHA256 от строки "{ts}.{body}" с секретом подписи магазина и сравните с заголовком. Секрет (whsec_…) у каждого магазина свой, он виден администратору в кабинете: «Настройки → API и магазины», там же его можно перевыпустить. Для магазинов, созданных до обновления, подпись прежняя — по ключу организации, пока вы не перевыпустите секрет.

06

Список заказов, карточка и этикетка

GET/orders?page=1&page_size=20&status=in_transit

Заказы вашей организации, новые сверху. Фильтры: status, search (номер, телефон или имя получателя), date_from / date_to (YYYY-MM-DD), tc_id; page_size — до 100.

{ "orders": [ { "id": 42, "uuid": "…", "status": "in_transit", … } ],
  "total": 1, "page": 1, "page_size": 20 }
GET/orders/{id}

Заказ с историей статусов — по id, uuid, номеру отправления или внутреннему номеру. Формат ответа тот же, что у /tracking/….

GET/orders/{id}/label

PDF для печати. По умолчанию — официальный штрихкод перевозчика, а если он ещё не готов — запасная этикетка Logtor (заголовок ответа X-Label-Source: cdek или logtor). Параметр kind: auto (по умолчанию), cdek — только официальная (до готовности ответ 503), logtor — только запасная.

06.1

Отмена заказа

POST/orders/{id}/cancel

Магазин может отменить заказ, пока отправление ещё не передано перевозчику в работу. Вместо {id} подойдёт id, uuid, номер отправления или внутренний номер заказа. Статус меняется только после подтверждения отмены перевозчиком; сумма возвращается на ваш счёт.

POST /api/v1/public/orders/42/cancel
X-Api-Key: <ключ>

→ 200 { "status": "cancelled", "order_id": 42 }

Если перевозчик уже принял заказ в работу и отмена невозможна через API, либо подтверждения отмены пока нет — ответ будет 400 с понятным объяснением, статус и деньги не меняются.

06.1

Повторная доставка уведомлений (retry)

Надёжная доставка событий

Если ваш сервер в момент отправки недоступен — Logtor гарантированно повторяет доставку уведомления:

  • до 8 попыток с экспоненциальной паузой (до 6 часов)
  • у каждого события свой event_id — повтор не создаст дублей у вас
  • после исчерпания попыток доставка помечается как неуспешная (dead-letter)
  • вы всегда можете получить актуальный статус сами (см. ниже)
06.2

Самостоятельный запрос статуса (pull)

Если уведомление потерялось или вы хотите синхронизироваться вручную — просто запросите текущий статус заказа по трек-номеру:

GET/tracking/{tracking_number}
GET /api/v1/public/tracking/1234567890
X-Api-Key: <ключ>

→ 200
{
  "order": { "id": 42, "tc_tracking_number": "1234567890",
             "status": "in_transit", "client_cost": 345.0 },
  "tracking": [
    { "status": "created",   "description": "Заказ создан", "event_time": "…" },
    { "status": "in_transit","description": "В пути", "location": "Москва", "event_time": "…" }
  ]
}

Это pull-механизм: вы всегда можете получить свежий статус и историю, даже если вам показалось, что какой-то вебхук потерялся.

07

Ошибки

{ "detail": "Текст понятной ошибки" }
  • 400неверный запрос / бизнес-правило (нет ПВЗ, нет согласия ПДн)
  • 401неверный API-ключ или аккаунт ещё не подтверждён оператором
  • 402недостаточно средств на балансе или исчерпан лимит постоплаты — оплатите счёт и повторите
  • 404заказ / отправление не найдено
  • 429слишком много запросов (rate limit)
  • 503перевозчик временно недоступен
07.0

Оплата и баланс

По умолчанию отправления оформляются по договору Logtor и оплачиваются постоплатой: стоимость каждого заказа учитывается на вашем счёте в момент его создания, а за период мы выставляем счёт по выписке. Размер лимита задолженности согласуется при подтверждении аккаунта. При отмене заказа или отказе перевозчика сумма возвращается на счёт.

  • если лимит задолженности исчерпан (или баланса не хватает при работе по балансу), создание заказа вернёт 402 — заказ не создаётся
  • новый аккаунт подтверждает оператор: до этого API-ключи отвечают 401, а создание заказа в кабинете — 403
  • выписку за период (JSON или CSV для Excel) можно получить в кабинете или через GET /api/v1/tenants/me/billing/statement
  • остаток и историю операций видно в личном кабинете, раздел «Баланс и договор»
  • есть собственный договор с перевозчиком — подключите его там же: заказы пойдут от вашего имени, без наценки Logtor и без списаний с баланса
07

Rate limits

Для магазинов (с API-ключом): 120 запросов в минуту на ключ. Для крупных магазинов лимит поднимается по заявке, без смены кода. Запросы с неверным ключом считаются отдельно по IP (30 в минуту).

Публичные эндпоинты (без ключа):

  • расчёт — 20/мин с IP
  • пункты выдачи — 60/мин с IP
  • трекинг — 30/мин с IP
  • анонимные заказы — 5/час с IP + общий суточный лимит 50
07.1

Какие перевозчики показывать клиенту

Агрегатор перевозчиков

Logtor — агрегатор: под одной учёткой можно подключить несколько транспортных компаний. Сейчас работают СДЭК и DPD; Достависта, КСЭ и остальные появятся по мере запуска.

Для каждого клиента оператор платформы решает, какие перевозчики будут видны в его расчёте и кабинете:

  • подключено у платформы 5 ТК — а клиенту доступны только 1–2, остальные скрыты
  • у каждого перевозчика своя наценка (в % и/или в ₽), она видна в итоговой цене расчёта
  • клиент всегда видит «чистые» тарифы только тех перевозчиков, которые ему открыты

Смена набора перевозчиков для клиента не ломает кабинет: скрытые ТК остаются подключёнными, но не участвуют в расчёте. Это удобно, когда магазину нужна только одна служба доставки, а вы хотите потом открыть ему остальные.

07.2

Подсказка городов в калькуляторе

В калькуляторе (на сайте и в кабинете) поля городов имеют подсказки: вводите первые буквы — появляется список. Список берётся из справочника подключённых перевозчиков, поэтому в нём есть и малые населённые пункты (Одинцово, Батайск и т.п.), а не только крупные города. Это убирает ошибки в написании и сразу даёт корректный расчёт.

GET/cities?q=Один

Ответ — массив (с заголовком X-Api-Key — по вашим перевозчикам):

[
  { "name": "Одинцово", "full_name": "Одинцово, Одинцовский р-н, Московская обл., Россия" },
  { "name": "Одинцовский", "full_name": "Одинцовский, Одинцовский р-н, Московская обл., Россия" }
]

name подставляется в поле расчёта, full_name — подпись с регионом для списка. Без ключа адрес /api/v1/public/cities?q=… работает для лендинга с лимитом 60 запросов в минуту на IP.

07.3

Подсказка улиц и домов

У перевозчиков справочника улиц нет — они отдают только города и пункты выдачи. Поэтому подсказки адресов приходят из отдельного справочника и работают так же, как подсказки городов: вводите первые буквы, появляется список.

GET/addresses?q=Тверская 7&city=Москва
{
  "available": true,
  "items": [
    { "value": "Тверская улица, 7", "street": "Тверская улица", "house": "7", "kind": "house" }
  ],
  "provider": "photon"
}
  • value подставляется в поле адреса как есть
  • kind: пока в запросе нет цифры — street, появилась цифра — house
  • city сужает выдачу; без него подсказки могут прийти из другого региона
  • available: false — справочник отключён; это не ошибка, поле работает как обычный ввод
  • минимум 3 символа, ответы кешируются на 15 минут

Выбранный из подсказки адрес — это строка для документов. Перевозчик всё равно сверит её со своим справочником при создании заказа.

08

Готовы подключаться?

Заведите кабинет и получите API-ключ магазина

Администратор аккаунта выдаёт ключ в настройках. Наценка показывается в расчёте и фиксируется в заказе.