Перейти до вмісту

Інтеграція з Укрпоштою в Python

Матеріал з K2 ERP Wiki

12.; технічна архітектура рішення для бізнесу

9.; Критерій !; |- | Валідація | Результат, список помилок.; |}

!; |- | format | varchar | PDF, PNG або інший формат.; №

db=db,

!; |- | created_at | timestamp | Дата події.; |- | city | varchar | Місто / населений пункт.; | Address classifier і ручна перевірка.; характеристика

18.3.; Графік актуалізація довідників

recipient_client=recipient_client,

!; !; |- | RouteUnavailableError | Маршрут доставки недоступний.; |- | width | numeric | Ширина.; №

"city": "Київ",


!; Створити клієнта-відправника.; користувач системи натискає «Створити відправлення Укрпошта» або спрацьовує автоматичне правило.; # Чи потрібна післяплата?; |- | AC-5 | платформа запускає синхронізацію відділень.; |- | Sender Client | Клієнт-відправник в API Укрпошти.; Python-сервіс виконує валідацію.; | Зберегти raw-відповідь.; |}

params = params or {}

Python Status Sync Worker

language: str = "ua"

POST /api/v1/ukrposhta/integrations

13.2.; Основні методи Python-клієнта

|- | AC-18 | Менеджер відкриває dashboard.; Пріоритет !; * Як почати роботу з API, реліз 11.02.2026.; Критерій !; |- | Некоректна адреса | API має змогу повернути помилку.; | style="background:#c8e6c9;" | Норма |- | Повернення | Відправлення повертаються.; response = await ukrposhta_client.create_shipment(payload)

  • повна супровід міжнародних відправлень;
  • повна супровід внутрішніх листів;
  • складна робота з митними даними;
  • повна автоматизація процесів післяплати;
  • складний UI складу;
  • власний компонент адресного класифікатора без API;
  • інтеграційні функціональні можливості з фінансовими сервісами;
  • масова оптимізація логістики.; Відправлення, статуси, ярлики

Управлінський результат: менеджер, складський облік і керівник повинні бачити, скільки відправлень створено, скільки ярликів надруковано, які посилки доставлені, які повертаються, які мають помилки адреси, індексу, оплати або статусу.; | style="background:#bbdefb;" | В роботі |- | Прибуло | Очікує отримувача.; |- | user_token_encrypted | text | Зашифрований user token.; №

істотно: для роботи з API Укрпошти використовуються user token та authorization bearer, які потрібно отримати після підписання договору.; | style="background:#bbdefb;" | Групова |}

pass

Після створення відправлення платформа повинна отримати супровідний документ / sticker / label.; | Python-сервіс створює відправлення.; | Перевести в NEEDS_RETRY.; |}

Приклад hash:

"barcode": shipment.barcode,
; Де застосовується
"postcode": "79000",
id uuid Внутрішній ID.; №

8.; Поле

data={

До MVP не входить:

}
)
payload={"shipment_id": str(shipment.id)},

</syntaxhighlight>

7.; Основні сутності

GET /api/v1/ukrposhta/postcodes?query=01001
AC-1 Адміністратор створює інтеграцію Укрпошти.; Дія
db.commit()
audit_logger.log(
- raw_response jsonb Відповідь.; )
"phone": "+380501112233",

21.; Модель даних

pass
- label_base_url varchar Статус стає DELIVERED і підсвічується зеленим.; "external_order_id": command.external_order_id,

Для реалізації задачі необхідно отримати:

method: str,

15.11.; Синхронізація статусів

21.6. ukrposhta_events

external_order_id - Зміна API - DuplicateShipmentError - Друк ярлика - weight numeric Вага.;=== Етап 8.; Production hardening ===
user_token: str
self.user_token = user_token

Критично істотно: інтеграційні функціональні можливості не повинна створювати дублікати відправлень.; | Черга, API-статуси, транспортування.; Як зменшити

- provider varchar ukrposhta.; Код Статус змінюється на LABEL_PRINTED.; Подія
verify_ssl: bool = True

24.1.; Основні KPI

AC-7 K2 ERP передає валідне замовлення.; Колір

9.; Типи відправлень

- Невідомі статуси - Directory Sync Worker style="background:#f3e5f5;" | Фіолетовий
Скасовано CANCELLED style="background:#c8e6c9;" | Зелений
Зареєстровано REGISTERED - AC-17 API повертає невідомий статус.; entity_type="ukrposhta_shipment",
"address": {
method=method,


щоб контролювати якість логістики, повернення, затримки та помилки.; характеристика

Неправильний token інтеграційні функціональні можливості не працюватиме.; Розділ API

щоб наклеїти його на посилку.; !; |-

Адресний класифікатор UNKNOWN_STATUS, ручна перевірка.; Worker створює адресу відправника, якщо її ще немає.; |- Міжнародні відправлення складніші Потрібні митні й країнові поля.; Компонент
"region": "Київська",
; K2 ERP створює замовлення.; Одержувач
}
"raw_request": command.model_dump(),

Ключі дедублікації:

id uuid Dashboard, список відправлень, картка замовлення.; | платформа повертає успішний або помилковий статус.; Очікуваний результат
"declared_price": 1500.00,

створення відправлень забезпечується через Головна ідея: розробити Python-сервіс, який інтегрує K2 ERP / CRM / інтернет-магазин / WMS з API Укрпошти; додатково реалізовано адрес, клієнтів, друку супровідних ярликів, розрахунку вартості, трекінгу статусів і контролю доставки.; Сутність

"Content-Type": "application/json",
  • timeout;
  • HTTP 429;
  • HTTP 500;
  • HTTP 502;
  • HTTP 503;
  • HTTP 504;
  • тимчасової недоступності API;
  • тимчасової помилки друку ярлика;
  • тимчасової помилки синхронізації статусів.; # Чи потрібно зберігати адреси одержувачів у K2 ERP?; Тип

GET /api/v1/ukrposhta/post-offices?postcode=01001 4.; shipment.status = "NEEDS_RETRY"

def create_address(self, payload: "AddressPayload") -> "AddressResponse":
  • інший формат створення;
  • митний характеристика;
  • категорію вкладення;
  • країну призначення;
  • англомовні адресні поля;
  • обмеження по вазі;
  • додаткові статуси;
  • друк митних або супровідних документів.; Тип
"client_id": "sender-client-id",

6.1.; Створення відправлення з K2 ERP

):
- old_status varchar Barcode зберігається в K2 ERP.; |- print_count integer }

POST /api/v1/ukrposhta/directories/sync

3.; Основні функціональні можливості API Укрпошти

"postpay_amount": command.delivery.postpay_amount,
pass

UKRPOSHTA_BASE_URL=https://www.ukrposhta.ua/ecom/0.0.1/

AC-11 - AuthError - ukrposhta_status varchar - sms_enabled boolean - Audit Logger Check-connection і повідомлення адміністратору.; API повертає shipment_id / barcode.; Поле ; Очікуваний результат
</div>

 shipment.delivered_at = utc_now()

=== 24.3.; Проблемні відправлення ===
=== Етап 4.; Адреси та клієнти ===
<pre>
 },

!; |-
| актуалізація довідників
| Низький
| Регламентна задача.; shipment.error_message = str(exc)
 bearer_token: str,
<syntaxhighlight lang="python">
 bearer_token: str
{
=== 22.2.; Створення відправлення ===
 try:
 }
 shipment = shipment_repository.create(
!; |-
| style="background:#bbdefb;" | Блакитний
| #bbdefb
| операційна дія виконується або доставка в процесі.; |-
| printed_by
| uuid
| користувач системи.;[[Категорія:K2 ERP]]

* додати rate limiting;
* додати моніторинг;
* додати alerting;
* додати dead letter queue;
* додати резервне копіювання;
* додати безпечне зберігання секретів.;
- name varchar - height numeric - Tracking Worker - address_id uuid Адреса.; Коментар

10.; Статуси відправлень

31.; Відкриті питання

15.5.; Пошук відділень

shipment = shipment_repository.get_by_barcode(db, barcode)
; Поле style="background:#c8e6c9;" | Зелений
Ярлик надруковано LABEL_PRINTED - AC-9 } Повернення, retry, неправильна адреса.; | style="background:#fff9c4;" | Жовтий
Доставлено DELIVERED - raw_response jsonb інтеграційні функціональні можливості зберігається в системі.; |- entity_id uuid style="background:#eeeeee;" | Сірий
Очікує створення PENDING_CREATE - Пошук відділень та індексів - building varchar Будинок.; headers=headers,
pass
def create_shipment(self, payload: "ShipmentPayload") -> "ShipmentResponse":

15.3.; Синхронізація адресних довідників

14.; "shipment_type": "DOMESTIC_PARCEL", Retry заборонений для:

- AC-19 - AC-2 Очікує створення, прибуло.; Частота актуалізація
entity_id=shipment.id,
self,
|-
| ValidationError
| Некоректні інформаційні дані замовлення.; характеристика
 "sender": {
10.; Критерій
 db: "Session",
офіційно затверджений портал API Укрпошти містить такі ключові напрями:

 pass

6.; |-
| raw_request
| jsonb
| Запит.; |-
| client_type
| varchar
| sender або recipient.; |-
| PhoneValidationError
| Некоректний телефон одержувача.; | Відправлення не створюється без виправлення.; {| class="wikitable"

!; | style="background:#ffcc80;" | Помаранчевий
|-
| Невідомий статус
| UNKNOWN_STATUS
| API повернув статус, якого немає в мапінгу.; Очікуваний результат
2.; |-
| AC-10
| Адреса некоректна.; |-
| Синхронізація статусів
| Середній
| Фоновий бізнес-процес.; |-
| AC-13
| Ярлик друкується повторно.; я хочу бачити статистику по доставках Укрпоштою, 
 delivery_queue.enqueue(
 status_response = await ukrposhta_client.track_shipment(barcode)
|-
| Створення відправлення
| Високий
| ключовий бізнес-процес відвантаження.; Призначення
!; |}

UKRPOSHTA_TIMEOUT_SECONDS=30

import httpx

* створення інтеграції Укрпошти;
* перевірка user token і bearer token;
* створення адреси відправника;
* створення адреси одержувача;
* створення клієнта-відправника;
* створення клієнта-одержувача;
* створення внутрішнього відправлення по Україні;
* збереження shipment_id / barcode;
* друк супровідного ярлика;
* синхронізація статусів;
* дедублікація;
* retry-механізм;
* журнал подій;
* dashboard API;
* базові unit-тести;
* mock API для інтеграційних тестів.; !; | Перевести в NEEDS_CORRECTION.; характеристика

!; | Друге відправлення не створюється.; |-
| style="background:#ffcc80;" | Помаранчевий
| #ffcc80
| Потрібна дія або є собою ризик.; | Потрібен для UI та валідації.; |-
| default_sender_client_id
| varchar
| Клієнт-відправник за замовчуванням.; | має змогу бути окремим модулем.; |-
| Shipment
| Відправлення в API Укрпошти.;=== 15.9.; Скасування / видалення відправлення ===

== 23.; Обробка помилок ==

 def track_shipment(self, barcode: str) -> "TrackingResponse":
Користувачі:
 old_status=old_status,
 return {}
|-
| Відправлення по Україні
| Створення внутрішніх поштових відправлень.;== 16.; Приклад запиту на створення відправлення ==

* неправильного user token;
* неправильного bearer token;
* помилок валідації;
* неправильного індексу;
* недоступного маршруту;
* некоректного телефону;
* відправлення, яке вже створене;
* відправлення, яке вже доставлене;
* відправлення, яке вже скасоване.; |-
| AC-15
| Відправлення доставлено.; Очікуваний результат
 "middle_name": "Іванович",
=== 12.1.; Загальна схема ===
!; |-
| default_sender_address_id
| varchar
| Адреса відправника за замовчуванням.;== Див.; 33.; додатково ==

 response = await client.request(

* отримати user token;
* отримати authorization bearer;
* перевірити доступ до API;
* отримати актуальну Swagger-документацію;
* перевірити створення адрес;
* перевірити створення клієнтів;
* перевірити створення тестового відправлення;
* перевірити друк ярлика;
* перевірити трекінг.; |-
| Друк ярлика
| Високий
| Потрібно для складу.; | застосовується розробниками.; |}

Ukrposhta API Client

 "recipient": {

1.; | style="background:#ffcc80;" | Помаранчевий
|-
| Потребує виправлення
| NEEDS_CORRECTION
| Некоректна адреса, індекс або інформаційні дані одержувача.; | Помилка API, неправильний token, недоступний маршрут.; |-
| Типи відправлень
| 1 раз на добу або після зміни API
| Потрібні для валідації.; |}

4.; 6.; | style="background:#fff9c4;" | Контроль
|-
| Доставлено
| Успішні доставки.; | Потрібен для валідації адрес.; | style="background:#c8e6c9;" | Норма
|-
| Ярлики надруковано
| Готові до пакування відправлення.; |}

=== Етап 5.; Відправлення і валідація ===

# Який тип договору та доступу до API застосовується?; | Перевести в NEEDS_CORRECTION.; Укрпошта

<pre>
<pre>
Retry дозволений для:
!; |-
| Validation Layer
| Перевіряє одержувача, адресу, індекс, вагу, оплату.; |-
| Помилка API
| Код, повідомлення, raw-відповідь.; |-
| AC-16
| Відправлення повертається.;=== Етап 7.; Dashboard та аудит ===
 self.bearer_token = bearer_token
UKRPOSHTA_LANGUAGE=ua
 new_status = ukrposhta_status_mapper.from_api(status_response)
|-
| Чернетка
| DRAFT
| Замовлення є собою в K2 ERP, але відправлення ще не створено.; | Довідник відділень оновлюється.; !; |-
| Дублювання відправлень
| Повторний запит має змогу створити друге відправлення.; |-
| Label / Sticker
| Супровідний ярлик.; Дата
 "apartment": null
== 6.; Основні бізнес-сценарії ==
PATCH /api/v1/ukrposhta/shipments/{shipment_id}
<pre>
{| class="wikitable"
{| class="wikitable"
 "postpay_enabled": true,
5.; | Повернути існуюче відправлення.; | Статус стає RETURNING і підсвічується помаранчевим.; 
POST /api/v1/ukrposhta/tracking/sync
API-документація Укрпошти описує послідовність створення простого відправлення так:
{| class="wikitable"
 if existing:
 self.timeout_seconds = timeout_seconds
== 32.; Джерела ==
!; |-
| TimeoutError
| Перевищено час очікування.; |}

<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">

 "external_order_id": "K2-ORDER-2026-000123",

<pre>

 v

 self.base_url = base_url.rstrip("/")
 "length": 30,
 shipment.error_message = str(exc)
[[Категорія:Технічні завдання]]

!; # Чи потрібно підтримувати декілька складів?; Тип

 db=db,
 if old_status != new_status:
 "address_id": "sender-address-id",

== 27. Acceptance Criteria ==

== 17.; Валідація перед створенням відправлення ==
 pass
 db.commit()

 "phone": "+380671112233",

 finally:
!; |-
| Створення клієнта
| Тип клієнта, API ID, відповідь.; |}

 new_status="CREATED",

!; характеристика

[[Категорія:Інтеграції]]

 }
 "status": "PENDING_CREATE",
 "weight": 2.5,

UKRPOSHTA_RETRY_BACKOFF_SECONDS=5

== 5.; Базовий workflow створення відправлення ==
{| class="wikitable"
=== 21.2. ukrposhta_addresses ===

</div>

!; |-
| Swagger
| Технічна документація API.; :contentReference [oaicite:2]{index=2}
{| class="wikitable"
|-
| AC-14
| Tracking API повертає новий статус.; def get_label(self, shipment_id: str, format: str = "pdf") -> bytes:

* реалізувати dashboard API;
* реалізувати список проблемних відправлень;
* реалізувати фільтри;
* реалізувати експорт, якщо потрібно.; |-
| base_url
| varchar
| URL API.; | Потрібен для синхронізації з K2 ERP.; |}

 return shipment

!; |-
| sent_at
| timestamp
| Дата створення в API.; !; # Чи потрібно автономно сповіщати клієнта?; |-
| declared_price
| numeric
| Оголошена вартість.; Ключ

 "building": "10",
== 25.; Безпека ==
=== 27.5.; Статуси ===
 pass
щоб платформа автономно створила відправлення без ручного введення в кабінеті або іншій системі.; |-
| AddressError
| Некоректна адреса.; Кожне замовлення.; old_status="CREATING",
 task_name="send_ukrposhta_shipment",
== 14.; Конфігурація клієнта ==
 existing = shipment_repository.get_by_idempotency_key(

 pass
=== 15.2.; Перевірка підключення ===

!; Label Service отримує супровідний ярлик.; |-
| style="background:#fff9c4;" | Жовтий
| #fff9c4
| Очікування або прибуття.; Довідник

 "postcode": "01001",

<div style="border-left: 6px solid #6a1b9a; background: #f3e5f5; padding: 12px 16px; margin: 16px 0;">
я хочу надрукувати супровідний ярлик по створеному відправленню, 
Синхронізуються:

Міжнародні відправлення потрібно реалізовувати окремим модулем, з цієї причини що вони можуть мати:

 if response.content:
 shipment.sent_at = utc_now()
Як менеджер інтернет-магазину, 

!; shipment.status = "CREATED"
 )
class UkrposhtaSettings(BaseSettings):
 "width": 20,
 )
=== 21.4. ukrposhta_shipments ===
{| class="wikitable"
 pass
=== 15.12. Dashboard ===
 recipient_client = await client_service.ensure_recipient_client(shipment, recipient_address)
5.; | Зупинити інтеграцію, повідомити адміністратора.; Worker створює адресу одержувача.; | Статус стає UNKNOWN_STATUS і потрапляє в список ручної перевірки.; :contentReference [oaicite:3]{index=3}
== 20.; Черга створення відправлень ==

class UkrposhtaClient:

Приклад `.env`:

 shipment.ukrposhta_status = status_response.status
__TOC__
!;== 28. MVP ==

* https://dev.ukrposhta.ua/
* https://dev.ukrposhta.ua/documentation
* https://dev.ukrposhta.ua/faq
* https://dev.ukrposhta.ua/for-business
* API documentation 2025.; Заборонено зберігати їх у коді, Git, frontend-змінних або відкритих логах.; Замовлення
</pre>
Потрібно зберігати:
== 29.; Етапи реалізації ==
[[Категорія:Укрпошта]]

</div>

!; Метою задачі є собою створення Python-сервісу для інтеграції з Укрпоштою з метою автоматизації процесів доставки.; Python-сервіс:
</pre>
=== 20.1.; Логіка черги ===
Python Ukrposhta Integration Service
</pre>
 "postpay_amount": 1500.00,
<pre>
== 8. User Story ==
 def delete_shipment(self, shipment_id: str) -> "DeleteShipmentResponse":
 pass

!; command: "CreateUkrposhtaShipmentCommand",
== 19.; Дедублікація ==
|-
| API Layer
| REST API для прийому замовлень і команд із K2 ERP.; if new_status == "DELIVERED":

<pre>

 raise UkrposhtaApiError(response.text)

<div style="border-left: 6px solid #1565c0; background: #e3f2fd; padding: 12px 16px; margin: 16px 0;">
 "first_name": "Іван",
=== 15.1.; Створення інтеграції ===
|-
| Integration Account
| конфігурація API Укрпошти.; Створити відправлення або групу відправлень.; |-
| Відділення
| 1 раз на добу або за потреби
| Потрібні для вибору точки відправлення/видачі.; |-
| shipment_id
| ID відправлення в API Укрпошти.; |-
| entity_type
| varchar
| integration, shipment, address, client, label, directory.; |-
| shipment_hash
| Hash основних параметрів відправлення.; | Вони підсвічуються помаранчевим.;[[Категорія:Ukrposhta API]]
<pre>
платформа повинна логувати:

</pre>

 return

я хочу бачити статус доставки прямо в K2 ERP, 

!; |-
| district
| varchar
| Район.; | Print count збільшується, дія логуються.; Як керівник, 
{| class="wikitable"
!; |-
| phone
| varchar
| Телефон.; Тип

* створити FastAPI-проєкт;
* налаштувати PostgreSQL;
* створити моделі інтеграції, адрес, клієнтів, відправлень, ярликів, подій;
* налаштувати Alembic;
* реалізувати healthcheck.; # Чи потрібно друкувати ярлики автономно після створення?; | Вони підсвічуються червоним.; | style="background:#ffcc80;" | Потрібна дія
|}

 json: dict | None = None,

{| class="wikitable"
 def search_post_offices(self, filters: dict) -> "PostOfficeListResponse":
Сервіс повинен забезпечити:
 "name": "ТОВ «Відправник»",
|-
| Замовлень до відправки
| 42
| style="background:#fff9c4;" | Увага
|-
| Відправлень створено сьогодні
| 185
| style="background:#c8e6c9;" | Норма
|-
| Ярлики надруковано
| 172
| style="background:#c8e6c9;" | Норма
|-
| У дорозі
| 620
| style="background:#bbdefb;" | В роботі
|-
| Прибуло
| 88
| style="background:#fff9c4;" | Контроль
|-
| Доставлено
| 410
| style="background:#c8e6c9;" | Норма
|-
| Повернення
| 21
| style="background:#ffcc80;" | Потрібна дія
|-
| Помилки створення
| 5
| style="background:#ef9a9a;" | Критично
|-
| Потребують виправлення
| 9
| style="background:#ffcc80;" | Потрібна дія
|}

платформа повинна не допускати дублювання відправлень.; | style="background:#ef9a9a;" | Критично
|-
| Потребують виправлення
| Некоректні адреси, індекси, телефони.; Очікуваний результат

 entity_type="ukrposhta_shipment",
 pass

== 15.; API Python-сервісу ==
 event_type="UKRPOSHTA_STATUS_SYNCED",

async def create_ukrposhta_shipment(
=== 8.3.; Контроль статусів ===

<pre>
sha256(external_order_id + recipient_phone + recipient_postcode + declared_price + weight)

{| class="wikitable"
=== 15.4.; Пошук індексів ===

 )
 audit_logger.log(
!; 7.;</pre>
 "declared_price": command.delivery.declared_price,
 "apartment": "5"
|-
| Внутрішня посилка
| DOMESTIC_PARCEL
| Відправлення по Україні.; |-
| shipment_id
| varchar
| ID відправлення в API Укрпошти.; |-
| PostcodeError
| Некоректний або недоступний індекс.; |-
| event_type
| varchar
| Тип події.; |-
| printed_at
| timestamp
| Дата друку.; | ключовий сценарій для MVP.; * API documentation: Internal letters.; Стан
{| class="wikitable"

=== 21.3. ukrposhta_clients ===
=== 15.8.; актуалізація відправлення ===
</pre>
{| class="wikitable"
 params=params,
!;<div style="border-left: 6px solid #2e7d32; background: #e8f5e9; padding: 12px 16px; margin: 16px 0;">

* реалізувати створення відправлення;
* реалізувати мапінг K2 ERP  API Укрпошти;
* реалізувати валідацію;
* реалізувати hash відправлення;
* реалізувати дедублікацію.; |-
| AC-8
| API повертає barcode.; |-
| region
| varchar
| Область.;=== 22.3.; Worker створення відправлення ===

</pre>

 "weight": command.delivery.weight,

* створено;
* зареєстровано;
* прийнято;
* у дорозі;
* прибуло;
* доставлено;
* вручено;
* повертається;
* повернуто;
* скасовано;
* помилка;
* невідомий статус.;=== 27.3.; Створення відправлення ===
{{SEO
|title=Технічне завдання: Інтеграція з Укрпоштою для Python
|description=Технічне завдання на реалізацію Python-сервісу для інтеграції K2 ERP, CRM або інтернет-магазину з API Укрпошти: створення відправлень, адреси, клієнти, ярлики, тарифи, статуси, трекінг, міжнародні відправлення, dashboard та журналювання.
|keywords=Python, Укрпошта, Ukrposhta API, API Укрпошта, ТТН, ярлик, відправлення, доставка, K2 ERP, CRM, інтернет-магазин, FastAPI, логістика, поштовий індекс, трекінг
}}
!; |-
| idempotency_key
| Унікальний ключ конкретної спроби створення відправлення.; |-
| Міжнародні відправлення
| Створення міжнародних відправлень.; Поле

!; |-
| Mapping Layer
| Перетворює структури K2 ERP у формат API Укрпошти.;== 11.; Єдина логіка кольорів ==

 "height": 15,
</pre>

Критично істотно: якщо відправлення вже створене, повторний запит не повинен створювати нове відправлення.; |-

apartment varchar - created_at timestamp - Post Office Відділення Укрпошти.; 1.; №
<syntaxhighlight lang="python">
 shipment = shipment_repository.get_by_id(db, shipment_id)

 sender_address = await address_service.ensure_sender_address(shipment)

 "sms": true
 if not shipment:
!; |-
| style="background:#ef9a9a;" | Червоний
| #ef9a9a
| Помилка або негативний результат.; # Чи потрібно формувати групи відправлень?; Замовлення, замовник, адреса, вантаж
 entity_id=shipment.id,
 retry_backoff_seconds: int = 5
 |
 | 2.; |-
| external_address_id
| varchar
| ID адреси в API Укрпошти.; |-
| Створення відправлення
| Shipment ID, barcode, дата, відповідь API.; | платформа показує AuthError і не створює відправлення.; url=f"{self.base_url}/{path.lstrip('/')}",

'''Критично істотно:''' user token і authorization bearer потрібно зберігати тільки в зашифрованому вигляді або secret storage.; |}

 user_token: str,

!; |-
| API Event
| Подія інтеграції.; |}

=== 6.3.; Синхронізація статусів ===

 )

POST /api/v1/ukrposhta/routes/check-availability
{| class="wikitable"
 for barcode in barcodes:
<pre>
|-
| id
| uuid
| ID інтеграції.; Колір
<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">
 "last_name": "Петренко",

<syntaxhighlight lang="python">

я хочу натиснути кнопку «Створити відправлення Укрпошта», 
'''Для K2 ERP:''' цей workflow потрібно приховати від користувача.; # Чи потрібно підтримувати декілька відправників?; |-
| Delivery Order
| Замовлення на доставку в K2 ERP.; |-
| sender_client_id
| uuid
| Клієнт-відправник.; |-
| barcode
| Штрихкод / номер відправлення.; |-
| new_status
| varchar
| Новий статус.; |-
| barcode
| varchar
| Штрихкод / номер відправлення.; |-
| Sender Address
| Адреса відправника.; | Перевести в NEEDS_CORRECTION.; |-
| Client Service
| Створює та кешує клієнтів відправника й одержувача.; Для них потрібно передбачити окремий тип документа, оскільки листи мають інший життєвий цикл та власні параметри.; |-
| status
| varchar
| Статус K2 ERP.; Значення
=== 20.2.; Пріоритети задач ===

 def create_client(self, payload: "ClientPayload") -> "ClientResponse":

Ukrposhta Client  це Python-клас або пакет, який інкапсулює роботу з API Укрпошти.; | style="background:#ffcc80;" | Помаранчевий
|-
| Помилка
| ERROR
| Помилка створення або синхронізації.; |-
| Dashboard API
| інформаційні дані для менеджера, складу та керівника.; Очікуваний результат
}

 def create_shipment_group(self, payload: "ShipmentGroupPayload") -> "ShipmentGroupResponse":

 shipment.shipment_id = response.id
 shipment.status = new_status

K2 ERP / Dashboard / складський облік / Менеджер
!; Критерій

 v

!; характеристика

<pre>
{| class="wikitable"

=== Етап 3.; Ukrposhta Client ===
 path: str,

 ukrposhta_validator.validate(command)
!; |-
| Shipment Service
| Створює відправлення або групи відправлень.; |-
| Отримання статусу
| Старий статус, новий статус, джерело.; |-
| Перевірка маршруту
| Середній
| Валідаційний бізнес-процес.; |-
| Recipient Client
| Клієнт-одержувач в API Укрпошти.; |-
| Створення адреси
| Тип адреси, API ID, відповідь.; * зберігання налаштувань інтеграції;
* перевірку підключення до API;
* створення адреси відправника;
* створення адреси одержувача;
* створення клієнта-відправника;
* створення клієнта-одержувача;
* створення відправлення або групи відправлень;
* друк супровідних документів / ярликів;
* розрахунок вартості доставки, якщо підтримується API;
* перевірку доступності маршруту доставки;
* пошук поштових індексів;
* пошук відділень;
* отримання статусів відправлень;
* синхронізацію статусів назад у K2 ERP;
* підтримку внутрішніх відправлень по Україні;
* підтримку міжнародних відправлень як окремого сценарію;
* журналювання всіх API-запитів;
* dashboard для контролю логістики.; | Черга, retry, dashboard помилок.; |-
| Tracking Status
| Статус доставки.; | style="background:#eeeeee;" | Сірий
|}

{| class="wikitable"

GET /api/v1/ukrposhta/shipments/{shipment_id}/label

 self,
 },
<pre>
<div style="border-left: 6px solid #2e7d32; background: #e8f5e9; padding: 12px 16px; margin: 16px 0;">
POST /api/v1/ukrposhta/shipments/{shipment_id}/cancel
|-
| style="background:#c8e6c9;" | Зелений
| #c8e6c9
| Успішно: створено, зареєстровано, ярлик надруковано, доставлено.; | Окремий сценарій із власними правилами.; |-
| external_client_id
| varchar
| ID клієнта в API Укрпошти.; |-
| Внутрішні листи
| Окремий API-сценарій для листів.; Надрукувати супровідні документи / ярлик.; Тип

=== 21.5. ukrposhta_labels ===

=== 6.2.; Друк ярлика ===

<syntaxhighlight lang="python">

* індекс;
* область;
* район;
* населений пункт;
* вулицю, якщо доступна;
* відділення, якщо прив'язане;
* ознаку активності;
* дату актуалізація.; |-
| Скасування відправлення
| Високий
| істотно до передачі посилки.; K2 ERP отримує номер відправлення.; # Чи потрібно експортувати журнал відправлень в Excel?; |-
| payload
| jsonb
| Технічні інформаційні дані.; Статус

 recipient_address = await address_service.ensure_recipient_address(shipment)

* менеджер;
* комірник;
* оператор складу.; |-
| style="background:#eeeeee;" | Сірий
| #eeeeee
| Чернетка, скасовано або неактивно.; # Чи потрібні тільки внутрішні відправлення по Україні?; Колір
 "description": "Одяг",

<pre>
!; | Повторити фоново.; характеристика

=== 8.1.; Створення відправлення ===
 v
Python-сервіс регулярно отримує статуси відправлень і оновлює K2 ERP.; |-
| shipment_type
| varchar
| DOMESTIC_PARCEL, INTERNATIONAL_PARCEL тощо.; характеристика
!; характеристика

 entity_id=shipment.id,
<pre>
</div>
До MVP входить:
|-
| id
| uuid
| ID події.; Коментар
 payload=status_response.raw_payload,
POST /api/v1/ukrposhta/integrations/{integration_id}/check-connection

 "city": "Львів",

== 1.; Мета ==
[[Категорія:Python]]
</pre>

 sender_client=sender_client,

 "street": "Хрещатик",
!; |-
| AC-3
| Token неправильний.; Причина

* перевіряє замовлення;
* перевіряє одержувача;
* перевіряє адресу та індекс;
* створює або знаходить адресу відправника;
* створює або знаходить адресу одержувача;
* створює або знаходить клієнта-відправника;
* створює або знаходить клієнта-одержувача;
* створює відправлення;
* зберігає barcode / shipment UUID / номер відправлення;
* формує супровідний ярлик;
* передає номер відправлення назад у K2 ERP.; |-
| Shipment Group
| Група відправлень.; |-
| Відстеження відправлень
| Отримання статусів доставки.; |-
| style="background:#f3e5f5;" | Фіолетовий
| #f3e5f5
| Спеціальний або невідомий статус.; Python-сервіс зберігає номер відправлення.;=== 13.1.; Призначення ===

* реалізувати створення адреси відправника;
* реалізувати створення адреси одержувача;
* реалізувати кешування адрес;
* реалізувати створення клієнтів;
* реалізувати повторне використання клієнтів.; | Валідація індексу до API-запиту.; |-
| Адресний класифікатор
| 1 раз на добу або за регламентом
| має змогу бути великим довідником.;=== 6.5.; Внутрішні листи ===
</pre>
 params.setdefault("token", self.user_token)

 shipment.status = "ERROR"
=== Етап 1.; Аналіз API Укрпошти ===
== 18.; Адресний класифікатор і довідники ==

 shipment.barcode = response.barcode
!;</div>

* PDF або інший формат, який повертає API;
* файл зберігається у картці відправлення;
* статус змінюється на LABEL_PRINTED;
* дія логуються в аудиті.; |}

</pre>

== 22.; Приклад Python-логіки ==
<syntaxhighlight lang="python">
{{DISPLAYTITLE:Технічне завдання: Інтеграція з Укрпоштою для Python}}
 headers = {
|-
| id
| uuid
| Внутрішній ID.; KPI
=== 27.2.; Довідники ===

!; |}

=== 15.7.; Створення відправлення ===

async def send_ukrposhta_shipment(shipment_id: str, db: "Session") -> None:
!; Поле

 def check_connection(self) -> "ConnectionStatus":
інтеграційні функціональні можливості призначена для:
 payload={"external_order_id": command.external_order_id},
|-
| Створення запиту на відправлення
| Замовлення, одержувач, індекс, адреса, сума.; Як менеджер, 

=== 18.2.; Відділення ===
 pass
</div>

=== 15.6.; Перевірка маршруту між індексами ===
 shipment.status = "CREATING"

 def __init__(
== 2.; Область сфера застосування ==

 "idempotency_key": command.idempotency_key,
11.; shipment.raw_response = response.raw_payload
 |
 | 5.; base_url: str = "https://www.ukrposhta.ua/ecom/0.0.1/"
!; | style="background:#ffcc80;" | Важлива
|-
| Внутрішній лист
| DOMESTIC_LETTER
| Лист по Україні.; характеристика
=== 18.1.; Поштові індекси ===
!; |-
| raw_response
| jsonb
| Відповідь API.; Створити адресу одержувача.; def check_route_availability(self, sender_postcode: str, recipient_postcode: str) -> "RouteAvailabilityResponse":
 v
=== Етап 6.; Статуси та друк ===
на `REGISTERED` виступає ключовою рисою У документації для міжнародних відправлень описується lifecycle із полями `status` та `statusDate`; після створення статус змінюється на `CREATED`, а після реєстрації.; | style="background:#ef9a9a;" | Червоний
|-
| Потребує повтору
| NEEDS_RETRY
| Технічна помилка, можна повторити.; | style="background:#c8e6c9;" | Норма
|-
| У дорозі
| Відправлення в транспортуванні.; |-
| error_message
| text
| Помилка.; |-
| Address Classifier
| Адресний класифікатор.; | style="background:#fff9c4;" | Додаткова
|-
| Група відправлень
| SHIPMENT_GROUP
| Пакетна передача декількох відправлень.; Поле
 timeout_seconds: int = 30
[[Категорія:API]]

K2 ERP передає в Python-сервіс замовлення, інформаційні дані одержувача, адресу, індекс, параметри вантажу та оплату.; |-
| length
| numeric
| Довжина.; "delivery": {

<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">

 )
 timeout_seconds: int = 30,
 ) -> dict:
!; !; | style="background:#bbdefb;" | Блакитний
|-
| Створено
| CREATED
| Відправлення створено в API.; Дія системи
|-
| 07.05.2026
| K2-ORDER-123
| -
| Іван Петренко
| style="background:#ef9a9a;" | Помилка
| Некоректний індекс
| Виправити адресу
|-
| 07.05.2026
| K2-ORDER-124
| 0500000000000
| Олена Сидоренко
| style="background:#ffcc80;" | Повернення
| Не отримано
| Контроль повернення
|-
| 07.05.2026
| K2-ORDER-125
| 0500000000001
| ТОВ «Альфа»
| style="background:#f3e5f5;" | Невідомий статус
| Новий статус API без мапінгу
| Оновити мапінг
|}

[[Категорія:Логістика]]

 "street": "Січових Стрільців",

* зберігання user token і bearer token тільки у secret storage або в зашифрованому вигляді;
* заборону логування token;
* HTTPS для всіх API-запитів;
* перевірку SSL;
* рольову модель доступу;
* окремі права на створення відправлення;
* окремі права на скасування відправлення;
* окремі права на друк ярлика;
* журнал усіх дій;
* захист від дублювання відправлень;
* маскування телефонів одержувачів у логах;
* контроль доступу до персональних даних.; |-
| Повторна операційна дія
| Хто запустив, причина, результат.; |-
| postcode
| varchar
| Поштовий індекс.; |-
| created_at
| timestamp
| Дата створення.; | Показати менеджеру.; |-
| street
| varchar
| Вулиця.; Менеджер натискає одну кнопку «Створити відправлення Укрпошта», а Python-сервіс сам виконує всі технічні кроки.; характеристика
'''Технічний стек:''' Python 3.11+, FastAPI, PostgreSQL, SQLAlchemy, Alembic, httpx, Pydantic, Celery/RQ/APScheduler, Redis, Docker.; Окремо варто відзначити кожне відправлення, повторний запит, друк ярлика, помилка API і зміна статусу повинні мати внутрішній ID, idempotency_key, журнал подій і контроль повторної обробки.; HTML
=== 27.4.; Друк ярлика ===
</div>
 },
 "address": {
== 26.; Логування та аудит ==
Як комірник,

23.1.; Типи помилок

base_url: str,

UKRPOSHTA_BEARER_TOKEN=********

22.4.; Синхронізація статусів

UKRPOSHTA_USER_TOKEN=******** UKRPOSHTA_LABEL_BASE_URL=https://www.ukrposhta.ua/ecom/0.0.1/

23.2.; Retry-логіка

3.; Тип помилки label_base_url: str | None = None

24.2.; Приклад dashboard

style="background:#ffcc80;" | Потрібна дія
Помилки API style="background:#fff9c4;" | Увага
Відправлень створено Кількість створених відправлень.; актуалізація статусів
def track_shipments(self, barcodes: list [str]) -> "TrackingListResponse":
def get_shipment(self, shipment_id: str) -> "ShipmentResponse":
pass

</syntaxhighlight>

щоб оперативно реагувати на повернення, затримки та проблемні доставки.; # Чи потрібна супровід внутрішніх листів?; |}

async with httpx.AsyncClient(timeout=self.timeout_seconds) as client:

27.6. Dashboard

  • підписаний договір з Укрпоштою;
  • user token;
  • authorization bearer;
  • доступ до API-документації;
  • тестове середовище або тестові інформаційні дані, якщо надаються;
  • інформаційні дані відправника;
  • адресу відправника;
  • контактну особу відправника;
  • правила оплати доставки;
  • правила післяплати;
  • правила міжнародних відправлень, якщо потрібні;
  • правила друку ярликів;
  • правила актуалізація статусів;
  • перелік сервісів доставки, які будуть використовуватись;
  • вимоги K2 ERP до збереження номерів відправлень.; Колір

4.; Передумови

2.; |-

postpay_amount numeric Не створювати відправлення, показати список помилок.; Ризик
- Recipient Address - TrackingError - is_active boolean style="background:#c8e6c9;" | Зелений
Повертається RETURNING - Address Service Створює та кешує адреси відправника й одержувача.; event_type="UKRPOSHTA_SHIPMENT_CREATED",

істотно: методи Python-клієнта є собою внутрішньою абстракцією.; * реалізувати синхронізацію статусів;

  • реалізувати друк ярлика;
  • реалізувати retry;
  • реалізувати збереження PDF.; |}
if response.status_code >= 400: except Exception as exc: Офіційна інструкція «Як почати роботу з API» описує базовий workflow: створити адресу відправника, створити адресу одержувача, створити клієнта-відправника, створити клієнта-одержувача, створити відправлення або групу відправлень, після чого надрукувати супровідні документи.; | Версіонування клієнта і contract-тести.; |-
shipment_id uuid Відправлення.; * інтернет-магазинів;
  • CRM;
  • ERP;
  • WMS;
  • складів;
  • служб доставки;
  • торгових компаній;
  • дистриб'юторів;
  • компаній, які створюють багато поштових відправлень;
  • компаній, які хочуть контролювати доставку прямо з K2 ERP.; entity_type="ukrposhta_shipment",

</syntaxhighlight>

return existing
}

) -> "UkrposhtaShipment":

pass
db.commit()
- file_id uuid UNKNOWN_STATUS і таблиця status_mapping.; },
def update_shipment(self, shipment_id: str, payload: "ShipmentPayload") -> "ShipmentResponse":
"idempotency_key": "K2-ORDER-2026-000123-ukrposhta-v1",

</syntaxhighlight>

6.4.; Міжнародні відправлення

Поштові індекси 1 раз на добу або за потреби style="background:#ffcc80;" | Помаранчевий
Повернуто RETURNED - external_order_id varchar - AC-12 Відправлення не створюється, статус NEEDS_CORRECTION.; |- email varchar - updated_at timestamp - Label Service style="background:#fff9c4;" | Додаткова
Рекомендований лист REGISTERED_LETTER - Недоступність API }

15.10.; Друк ярлика

def search_postcodes(self, filters: dict) -> "PostcodeListResponse":

UKRPOSHTA_RETRY_COUNT=3

payload = ukrposhta_mapper.to_shipment_payload(
id uuid class="wikitable" Він бачить замовлення, відправлення, доставки, повернення та помилки.; | K2 ERP оновлює статус відправлення.; |- ApiError API повернув помилку.; Tracking Worker оновлює статуси доставки.; Валідація, мапінг, дедублікація, черга

</syntaxhighlight>

- bearer_token_encrypted text style="background:#c8e6c9;" | Базова
Міжнародна посилка INTERNATIONAL_PARCEL Відправлення за кордон.; Показник

K2 ERP / CRM / Website / WMS

- AC-6 - Ukrposhta Client Python-клієнт для API Укрпошти.; Тип
retry_count: int = 3
!; | Idempotency key і перевірка external_order_id.; |-
| Некоректний індекс
| Відправлення має змогу не створитись.; Що зберігати

* наявність external_order_id;
* наявність idempotency_key;
* чи не створено вже відправлення для цього замовлення;
* user token активний;
* bearer token активний;
* ПІБ або назву одержувача;
* телефон одержувача;
* індекс одержувача;
* адресу одержувача;
* індекс відправника;
* адресу відправника;
* доступність маршруту між індексами, якщо застосовується перевірка;
* вагу більше 0;
* габарити більше 0, якщо обов'язкові;
* оголошену вартість;
* післяплату, якщо застосовується;
* SMS-опцію, якщо застосовується;
* коректність типу відправлення;
* коректність міжнародних полів, якщо це міжнародне відправлення.; Значення
=== 12.2.; Основні компоненти Python-сервісу ===
 db.commit()
!; Призначення
 payload={
Результат:
class UkrposhtaClient:
class UkrposhtaApiError(Exception):
old_status = shipment.status

27.1.; інтеграційні функціональні можливості

- recipient_client_id uuid Клієнт-одержувач.; event_type="UKRPOSHTA_SHIPMENT_QUEUED",
  • реалізувати базовий request method;
  • реалізувати create_address;
  • реалізувати create_client;
  • реалізувати create_shipment;
  • реалізувати get_label;
  • реалізувати track_shipment;
  • реалізувати check_route_availability;
  • реалізувати обробку помилок.; Критерій
Замовлень до відправки class="wikitable"
continue
  • ID або код відділення;
  • поштовий індекс;
  • назву;
  • адресу;
  • населений пункт;
  • координати, якщо доступні;
  • графік роботи;
  • ознаку активності;
  • доступні сервіси.; | Архів, чернетки.; характеристика

30.; Ризики

style="background:#bbdefb;" | Блакитний
Прибуло ARRIVED - Скасування }
except TemporaryUkrposhtaError as exc:

from pydantic_settings import BaseSettings

; Worker створює клієнта-одержувача.; Тип
audit_logger.log(

13.; new_status="PENDING_CREATE",

params: dict | None = None, shipment=shipment, 12.; | style="background:#bbdefb;" | Блакитний
У дорозі IN_TRANSIT Відправлення рухається.; характеристика

3.; |-

idempotency_key varchar Довідник індексів оновлюється.; |} if shipment.status in ["CREATED", "REGISTERED", "DELIVERED"] and shipment.barcode: async def sync_ukrposhta_statuses(barcodes: list [str], db: "Session") -> None: idempotency_key=command.idempotency_key, new_status=new_status,
AC-4 платформа запускає синхронізацію індексів.; * Swagger-документація Укрпошти.; Статус K2 ERP

21.1. ukrposhta_integrations

"building": "1",
- name varchar - postpay_enabled boolean Чи є собою післяплата.; Тип задачі
)

Перед створенням відправлення платформа повинна перевірити: Потрібно зберігати: API Укрпошти має окрему документацію для внутрішніх листів.; Worker створює клієнта-відправника, якщо потрібно.; | style="background:#fff9c4;" | Жовтий

Створюється CREATING - delivered_at timestamp Дата доставки.; * API documentation: International shipments.; !; # Чи потрібні міжнародні відправлення?; API Укрпошти
pass
- source varchar K2_ERP, PYTHON_SERVICE, UKRPOSHTA, USER.; Коментар

GET /api/v1/ukrposhta/dashboard?date_from=2026-05-01&date_to=2026-05-31

"postpay_enabled": command.delivery.postpay_enabled,
sender_client = await client_service.ensure_sender_client(shipment, sender_address)

8.2.; Друк ярлика

return response.json()
v
"shipment_type": command.shipment_type,
"shipment_id": shipment.shipment_id,

платформа повинна забезпечити:

22.1.; Базовий API-клієнт

json=json,
- AC-20 є собою повернення.; Код платформа повертає PDF або інший доступний формат.; Реальні URI API потрібно брати з актуальної Swagger-документації Укрпошти.; "Authorization": f"Bearer {self.bearer_token}", ; Barcode Винести в окремий етап.; # Який формат друку потрібен: A4, термопринтер, PDF?; | style="background:#c8e6c9;" | Зелений
Передано Укрпошті ACCEPTED_BY_UKRPOSHTA Відправлення прийнято оператором.; Критерій
"region": "Львівська",

8.4.; Dashboard керівника

Етап 2.; Базовий Python-сервіс

13. Ukrposhta Client

24.; Dashboard менеджера і керівника

async def request( POST /api/v1/ukrposhta/shipments