Документація API

Інтеграція біржі SAMO-TRANS у вашу TMS або CRM

Документація API

Ідемпотентність

Заголовок Idempotency-Key: безпечні повтори запитів на запис без дублів.

Мережа ненадійна: відповідь може загубитися вже після того, як сервер обробив запит. Якщо просто повторити POST, з'явиться дубль заявки. Щоб цього не сталося, надішліть заголовок Idempotency-Key.

curl -X POST "https://api.samo-trans.com/api/public/v1/proposals/cargo" \
  -H "Authorization: Bearer $SAMO_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-88213-attempt-1" \
  -d '{ "startLocations": [ … ], "endLocations": [ … ], … }'

Заголовок необов'язковий і діє лише на запити, що змінюють дані (POST, PATCH, DELETE). На GET він ігнорується — читання й так безпечно повторювати.

Як обирати ключ

Ключ має бути стабільним для однієї логічної операції та різним для різних. Найнадійніше — взяти ідентифікатор із вашої системи:

Idempotency-Key: tms-order-88213

Не використовуйте випадковий рядок, згенерований під час повтору: тоді повтор виглядатиме як нова операція, і сенс заголовка зникає. Генеруйте ключ один раз, коли вирішили виконати операцію, і використовуйте його для всіх спроб.

Поведінка

СитуаціяРезультат
Перший запит із ключемВиконується звичайним чином, відповідь запам'ятовується на 24 години
Повтор: той самий ключ, те саме тілоПовертається збережена відповідь. Друга заявка не створюється
Повтор: той самий ключ, інше тіло422 з кодом validation_error — ключ уже використано для іншої операції
Повтор, поки перший запит ще виконується409 з кодом conflict — спробуйте ще раз за мить

Вікно захисту від паралельного дубля — 60 секунд. Якщо перший запит завершився раніше, наступний одразу отримає збережену відповідь.

Ключ діє в межах одного ключа API, методу й шляху: POST /proposals/cargo і POST /proposals/transport з однаковим Idempotency-Key — це дві різні операції.

Рекомендований цикл повтору

async function createWithRetry(payload, idempotencyKey) {
  for (let attempt = 0; attempt < 5; attempt += 1) {
    const res = await fetch(`${API_BASE}/api/public/v1/proposals/cargo`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.SAMO_KEY}`,
        "Content-Type": "application/json",
        // той самий ключ на всіх спробах — у цьому вся суть
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(payload),
    });

    if (res.ok) return res.json();

    const { error } = await res.json();
    // 409 — попередня спроба ще в польоті; 429 — ліміт. Обидва варті повтору.
    if (error.code === "conflict" || error.code === "rate_limited") {
      const retryAfter = Number(res.headers.get("Retry-After") ?? 0);
      await sleep(retryAfter * 1000 || 2 ** attempt * 500);
      continue;
    }
    throw new Error(`${error.code}: ${error.message} (requestId ${error.requestId})`);
  }
  throw new Error("giving up after 5 attempts");
}

Разом із пакетним створенням

POST /proposals/batch теж підтримує Idempotency-Key — і саме там він найкорисніший: повторна спроба після обриву зв'язку не створить 25 дублів, а поверне збережений результат першої спроби разом із поточним станом кожної позиції.