API quickstart
Публічний REST-API кабінету дає зовнішнім сервісам створювати й вести ТТН від імені вашого підключення. Нижче — усе, щоб зробити перший запит: як отримати ключ, як працюють скоупи, приклад «створити ТТН» трьома мовами, вебхуки й коди помилок.
Це неофіційний сервіс. Він не є сайтом АТ «Укрпошта» і не афілійований
з нею; кабінет лише проксує публічні API Укрпошти за вашими власними ключами. Машиночитана
специфікація — GET /openapi/v1.json.
Загальний флоу роботи з кабінетом — від ТТН і реєстру до претензій і бухгалтерії — у посібнику. Огляд можливостей — на сторінці можливостей, тарифікація відправлень — у тарифах. Це API кабінету поверх реального API самої Укрпошти під вашими ключами; як воно влаштоване й скільки ключів насправді потрібно — у розборі API Укрпошти.
1. Отримати ключ X-Api-Key
Автентифікація — один ключ у заголовку X-Api-Key. Ключ прив'язаний до
конкретного підключення (пари ваших ключів Укрпошти), тож усі ТТН
поїдуть саме тому відправнику.
Ключ випускається в кабінеті: Налаштування → «API-ключі». Керування доступне ролям Owner та Admin підключення.
Значення ключа показується рівно один раз при випуску — далі в базі лишається тільки його SHA-256-хеш, відновити ключ неможливо. Скопіюйте одразу; загубили — перевипустіть.
Формат ключа — upk_ і 64 hex-символи, напр. upk_a1b2c3d4….
У кабінеті поряд із ключем видно лише його перші символи для впізнаваності.
2. Скоупи
Кожен ключ має набір скоупів, а кожен ендпоінт вимагає відповідний скоуп через іменовану
політику (api:read / api:write / api:manage).
Набір зберігається канонічним рядком read,write,manage.
| Скоуп | Що дозволяє |
|---|---|
read |
Читання: список і картка ТТН, групи, розрахунок ціни, друковані форми (PDF). Виняток — акт ф.20 (він створюється, а не читається): потребує write. |
write |
Створення ТТН і груп. |
manage |
Операції над наявними ТТН (редагування, скасування, накладний платіж, отримувач, повернення), закриття реєстрів і виклик кур'єра. |
Ієрархія: manage ⊃ write ⊃ read.
Скоупи вкладені: ключ із write автоматично читає власні ресурси, а ключ із
manage виконує весь ланцюг (створення + читання + операції). Ієрархія діє
лише вниз: read не отримує write, а
write — manage.
Розкриття застосовується тільки при видачі claims під час
автентифікації — збережений у БД набір Scopes не змінюється (форма перегляду
ключа показує точний виданий набір). У формі випуску достатньо обрати найвищий потрібний
рівень: читання дістанеться саме тому рівню автоматично.
Ключу можна задати примусовий тарифний тип ТТН
(STANDARD/EXPRESS/DOCUMENT/CARGO) —
його виставляє менеджер B2B. Форс перебиває поле type у тілі запиту на всіх
шляхах, що задають тип (створення, batch, редагування й розрахунок ціни).
3. Авторизація й OpenAPI
Передавайте ключ у заголовку X-Api-Key на кожному запиті до /api/v1.
Інтерактивного UI (Swagger) немає — клієнт генерується з машиночитаної специфікації.
Специфікація OpenAPI доступна анонімно (без ключа), у т.ч. на проді:
GET /openapi/v1.json. Згодовуйте її генератору
клієнта (openapi-generator, NSwag, Kiota тощо). Той самий контракт відкрито для
LLM-агентів як MCP-сервер на /mcp з автентифікацією тим самим
X-Api-Key.
4. Приклад: створити ТТН
POST /api/v1/shipments (скоуп write). Тіло —
CreateShipmentRequest: тип доставки, отримувач, масив місць
(parcels) і ваш externalId (ключ ідемпотентності — повтор із тим
самим значенням повертає вже створену ТТН, а не дубль).
curl -X POST https://ukrposhta.main.fish/api/v1/shipments \
-H "X-Api-Key: upk_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{
"deliveryType": "W2W",
"recipient": { "clientUuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6" },
"parcels": [
{ "weightGrams": 500, "lengthCm": 10, "widthCm": 10, "heightCm": 10 }
],
"externalId": "order-1001"
}'
import requests
resp = requests.post(
"https://ukrposhta.main.fish/api/v1/shipments",
headers={"X-Api-Key": "upk_ваш_ключ"},
json={
"deliveryType": "W2W",
"recipient": {"clientUuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6"},
"parcels": [
{"weightGrams": 500, "lengthCm": 10, "widthCm": 10, "heightCm": 10}
],
"externalId": "order-1001",
},
timeout=30,
)
resp.raise_for_status()
print(resp.json()["barcode"])
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "upk_ваш_ключ");
var body = new
{
deliveryType = "W2W",
recipient = new { clientUuid = "3fa85f64-5717-4562-b3fc-2c963f66afa6" },
parcels = new[]
{
new { weightGrams = 500, lengthCm = 10, widthCm = 10, heightCm = 10 }
},
externalId = "order-1001",
};
var resp = await http.PostAsJsonAsync(
"https://ukrposhta.main.fish/api/v1/shipments", body);
resp.EnsureSuccessStatusCode();
var view = await resp.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(view.GetProperty("barcode").GetString());
Успіх → 200 з ShipmentView (там є barcode і
type — фактичний тарифний клас, що поїхав у Укрпошту). Без явного
groupId ТТН лягає у дефолтну групу-реєстр відправника під свій ефективний тип.
Створити одразу багато — POST /api/v1/shipments/batch (масив тих самих об'єктів).
5. Ключові ендпоінти
Найчастіші виклики й потрібний скоуп. Повний перелік із усіма формами й параметрами —
в GET /openapi/v1.json.
| Метод і шлях | Скоуп | Дія |
|---|---|---|
GET /api/v1/shipments | read | Список ТТН |
GET /api/v1/shipments/{id} | read | Одна ТТН |
POST /api/v1/shipments/price | read | Розрахунок вартості доставки |
GET /api/v1/shipments/{id}/sticker | read | Наклейка (PDF) |
GET /api/v1/address/regions | read | Області (довідник адрес) |
GET /api/v1/address/cities | read | Населені пункти (звуження за областю/районом) |
GET /api/v1/address/postoffices | read | Відділення (ВПЗ) населеного пункту |
POST /api/v1/shipments | write | Створити ТТН |
POST /api/v1/shipments/batch | write | Bulk-створення ТТН |
POST /api/v1/groups | write | Створити групу |
POST /api/v1/groups/{id}/close | manage | Закрити реєстр |
PUT /api/v1/shipments/{id} | manage | Редагувати ТТН |
DELETE /api/v1/shipments/{id} | manage | Скасувати ТТН |
POST /api/v1/courier/orders | manage | Виклик кур'єра |
Довідник адрес (/api/v1/address/*) читається з нашої копії довідника
Укрпошти — запити до самої Укрпошти тут не йдуть. Кожна відповідь несе відмітку свіжості
syncedAt і атрибуцію джерела source; дані походять із довідника
Укрпошти. Це неофіційний сервіс — він не є сайтом АТ «Укрпошта» і не
афілійований з нею.
6. Вебхуки: push замість опитування
Замість опитувати GET /api/v1/shipments/{id}, підпишіться на вебхук — і
отримуйте POST на свій endpoint при кожній зміні семантичного статусу ТТН
(trackingGroup).
Реєстрація — у кабінеті (не через API-ключ): сторінка «Ключі Укрпошти» →
розгорнути «Webhook» на потрібному підключенні → вказати https-URL і
«Зареєструвати». Керування — ролям Admin/Owner. На підключення діє рівно
один активний endpoint; URL має бути абсолютним https на публічний хост.
HMAC-секрет генерується при реєстрації й показується один раз. Кожен
запит несе заголовок X-Ukrposhta-Signature вигляду sha256={hex} —
це HMAC-SHA256 сирого тіла запиту вашим секретом (hex у нижньому регістрі).
Перевірка підпису
Рахуйте HMAC над отриманими байтами тіла (до будь-якого парсингу) і
звіряйте в постійному часі. Заголовки X-Ukrposhta-Timestamp і
X-Ukrposhta-Attempt підпис не покриває — у перевірці їх не
використовуйте.
import crypto from 'node:crypto'
function verify(rawBody, signatureHeader, secret) {
const expected =
'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(signatureHeader ?? '')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
import hashlib
import hmac
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header or "")
Ретраї та ідемпотентність
- Відповідайте 2xx швидко (таймаут на один POST ~10 с). Важку обробку — асинхронно: прийняли, поставили в чергу, повернули 2xx.
-
Невдала спроба ретраїться за розкладом
1m → 5m → 30m → 2h → 6h(усього 6 спроб: перша + 5 ретраїв). Після вичерпання доставка переходить у стан Dead і автоматично більше не надсилається — її можна повторити вручну кнопкою «Повторити» в кабінеті. -
Семантика — at-least-once: одну подію можна отримати кілька разів.
Робіть обробник ідемпотентним — дедуплікуйте за
X-Ukrposhta-Delivery(ретраї однієї доставки несуть той самий UUID). Рідкісний дубль із різнимиX-Ukrposhta-Deliveryвідсівайте за змістом:barcode+trackingGroup+eventDateUtc.
7. Коди помилок
Помилки бувають двох походжень: наші (автентифікація, скоупи, ліміти) і
Укрпошти (проксовані як є, коди UPExxxxx).
Наш рівень
| Статус | Коли |
|---|---|
401 |
Ключ відсутній або невалідний — немає заголовка X-Api-Key, ключ порожній/невідомий/відкликаний. |
403 |
Ключ валідний, але без потрібного скоупа для ендпоінта (код insufficient_scope). Напр., ключ лише з read на POST /shipments. |
429 |
Вичерпано ліміт частоти. Який саме бакет відмовив — у заголовку X-RateLimit-Scope; скільки чекати — у Retry-After та полі retryAfterSeconds. Backoff будуйте за code/retryAfterSeconds, а не за текстом error. |
Рівень Укрпошти
| Код | Значення |
|---|---|
UPE01001 | Тип ТТН не збігається з типом групи-реєстру (кабінет цього уникає дефолтною групою на кожен тип). |
UPE01002 | Габарити місця поза допустимими межами. |
UPE02001 | Клієнта (відправника/отримувача) не знайдено за вказаним ідентифікатором. |
Це лише найчастіші коди — повний каталог помилок UPExxxxx ведеться на боці
Укрпошти; кабінет повертає їх без змін разом із людиночитаним текстом.
Готові інтегруватися?
Створіть кабінет, підключіть sandbox-ключі, випустіть X-Api-Key і зробіть перший запит.