Статусы заказов FBS в CRM
Зачем
CRM или таск-трекер поддержки обычно должен знать статус заказа не хуже,
чем ваш личный кабинет — чтобы оператор поддержки видел «собирается» или
«доставлен» без переключения в другую систему. Опрашивать
GET /v1/orders целиком ради этого дорого (у крупного селлера —
до ~900 заказов в сутки); вебхук order.status_changed присылает
только то, что реально изменилось.
Подписаться на order.status_changed
POST https://api.perfektpak.ru/v1/webhooks
Authorization: Bearer $PP_KEY
Content-Type: application/json
{ "url": "https://your-crm.example/webhooks/perfektpak", "events": ["order.status_changed"], "description": "CRM — статусы заказов" }
Полный протокол регистрации (проверка URL, секрет, подпись) — на странице «Вебхуки». Ниже — то, что специфично именно для статусов заказов.
Обработать событие: previous_status
У событий *.status_changed поле data несёт не
только сам объект, но и previous_status — статус, из которого
заказ вышел. Это нужно, чтобы отличить, например, «собирается → в доставке»
от «собирается → отменён», не храня историю статусов у себя отдельно:
{
"id": "evt_9f2a1c",
"type": "order.status_changed",
"occurred_at": "2026-09-05T11:20:00+03:00",
"data": {
"previous_status": "ASSEMBLING",
"object": {
"id": "o_5f6a7b8c9d", "marketplace": "WB", "external_id": "WB-88213456",
"status": "IN_DELIVERY", "status_label": "В доставке",
"marketplace_status": { "supplier": "complete", "buyer": "sold", "changed_at": "2026-09-05T11:20:00+03:00" },
"article": "COFFEE-1KG-BRIZ", "barcode": "4680012340015", "nm_id": 123456789, "is_b2b": false,
"shipment_id": "s_wb_2026_0825_12", "shipment_external_id": "WB-1082345671",
"created_at": "2026-08-25T11:20:00+03:00", "packed_at": "2026-08-25T15:40:00+03:00",
"synced_at": "2026-09-05T11:20:00+03:00"
}
}
}
def handle_order_status_changed(event):
order = event["data"]["object"]
previous = event["data"]["previous_status"]
if order["status"] == "CANCELLED" and previous != "CANCELLED":
notify_support(order, reason="cancelled")
update_crm_card(order["external_id"], status=order["status"])
Резервный опрос
Доставка вебхука занимает время, а после восьмой подряд неудачной попытки подписка автоматически отключается. Держите резервный опрос — редкий, но достаточный, чтобы заметить «подписка отключилась и никто не увидел»:
import datetime
since = last_successful_sync() # ваша метка времени, не значение API
page = requests.get(
"https://api.perfektpak.ru/v1/orders",
params={"created_from": since.isoformat(), "created_to": datetime.datetime.now().isoformat(), "limit": 1000},
headers={"Authorization": f"Bearer {PP_KEY}"},
).json()
for order in page["items"]:
reconcile(order) # та же функция обновления карточки, что и у обработчика вебхука
Раз в час резервного опроса достаточно для большинства интеграций; если вебхук работает штатно, опрос почти всегда не найдёт расхождений — это ожидаемо, а не признак того, что опрос лишний.
Идемпотентность по id события
Ретраи доставки (см. расписание на странице «Вебхуки») могут прислать одно
и то же событие дважды — например, если ваш обработчик ответил успешно, но
соединение оборвалось раньше, чем мы это увидели. Каждое событие несёт
уникальный id (evt_...) — сохраняйте обработанные
id (последние сутки достаточно) и пропускайте повтор:
def on_webhook(event):
if already_processed(event["id"]):
return # ретрай того же события — не ошибка, просто не делаем работу дважды
handle_order_status_changed(event)
mark_processed(event["id"])
Что дальше
Заказ входит в отгрузку — если вам важен именно момент, когда маркетплейс принял всю партию, а не отдельный заказ, смотрите гайд «Отгрузки: узнать, что поставку приняли на СЦ».