Розробникам

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, а writemanage.

Розкриття застосовується тільки при видачі 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
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"
  }'
Python — requests
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"])
C# — HttpClient
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/shipmentsreadСписок ТТН
GET /api/v1/shipments/{id}readОдна ТТН
POST /api/v1/shipments/pricereadРозрахунок вартості доставки
GET /api/v1/shipments/{id}/stickerreadНаклейка (PDF)
GET /api/v1/address/regionsreadОбласті (довідник адрес)
GET /api/v1/address/citiesreadНаселені пункти (звуження за областю/районом)
GET /api/v1/address/postofficesreadВідділення (ВПЗ) населеного пункту
POST /api/v1/shipmentswriteСтворити ТТН
POST /api/v1/shipments/batchwriteBulk-створення ТТН
POST /api/v1/groupswriteСтворити групу
POST /api/v1/groups/{id}/closemanageЗакрити реєстр
PUT /api/v1/shipments/{id}manageРедагувати ТТН
DELETE /api/v1/shipments/{id}manageСкасувати ТТН
POST /api/v1/courier/ordersmanageВиклик кур'єра

Довідник адрес (/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 підпис не покриває — у перевірці їх не використовуйте.

Node.js
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)
}
Python
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 і зробіть перший запит.

Створити кабінет