Конвенції

Ця сторінка описує конвенції, спільні для всіх ендпоінтів /api/v1: базову адресу, формат відповідей, заголовки, пагінацію, роботу з датами та фільтрами. Якщо для конкретного ендпоінта конвенцію не перевизначено в Довіднику ендпоінтів — діє те, що описано тут.

База та версії

Базова адреса API — https://smengo.com/api/v1. Версія закріплена в шляху: зворотно несумісні зміни виходитимуть як /api/v2, а не як правки поточної версії — наявні інтеграції не зламаються непомітно.

API доступний лише по HTTPS. Відповіді не кешуються: кожна відповідь позначена Cache-Control: private, no-store, бо дані прив'язані до конкретного API-ключа.

Формат відповіді

Список ресурсів

Успішна відповідь для списку — об'єкт із data (масив) та meta.next_cursor:

{
  "data": [
    { "id": "9c1a2e34-5678-4abc-9def-0123456789ab", "...": "..." }
  ],
  "meta": { "next_cursor": "eyJrIjoiSXZhbm92YSIsImlkIjoiOWMxYS4uLiJ9" }
}

meta.next_cursor дорівнює null, якщо це остання сторінка.

Одиничний об'єкт

Ресурси, яких в організації рівно один (наприклад, GET /org), повертають об'єкт без meta:

{ "data": { "name": "Coffee & Co", "slug": "coffee-co", "timezone": "Europe/Kyiv" } }

Помилка

Усі помилки повертаються в єдиному envelope — незалежно від ендпоінта та причини:

{
  "error": {
    "code": "validation_error",
    "message": "Invalid query parameters.",
    "hint": "from must not be later than to.",
    "docs_url": "https://smengo.com/api-docs/reference/errors#validation_error",
    "request_id": "8f14e45f-9e05-4f1a-8a3e-3d2b1c0f9b1b"
  }
}

Повний каталог кодів помилок і що з кожним робити — на сторінці Помилки. Текстових відповідей у API немає — лише JSON, завжди, включно з помилками.

Заголовки відповіді

  • X-Request-Id — uuid v4, присутній на всіх відповідях /api/v1, включно з помилками. Вказуйте його, звертаючись у підтримку — за ним знаходиться конкретний запит у логах.
  • Cache-Control: private, no-store — відповіді не призначені для кешування ані браузером, ані проксі.

Пагінація

Списочні ендпоінти використовують cursor-пагінацію:

  • ?limit= — ціле число 1..200, за замовчуванням 100.
  • ?cursor= — непрозорий base64url-рядок із meta.next_cursor попередньої відповіді. Не збирайте його вручну — просто передавайте як є в наступний запит.
  • Порядок сортування фіксований для кожного ресурсу та завжди включає id як тай-брейкер (наприклад, співробітники сортуються за full_name, id). Тай-брейкер гарантує, що сторінки не дублюють і не пропускають рядки за однакових значень основного поля сортування.
  • Курсор, який зіпсований або належить іншому набору даних (наприклад, отриманий з іншими фільтрами) — 400 invalid_cursor.
curl -s "https://smengo.com/api/v1/employees?limit=50&cursor=eyJrIjoiSXZhbm92YSIsImlkIjoiOWMxYS4uLiJ9" \
  -H "Authorization: Bearer smg_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Обмеження частоти

Кожен API-ключ обмежений rate_limit_per_min запитів на хвилину — за замовчуванням 60, ліміт налаштовується індивідуально для конкретного ключа. Вікно ковзне (sliding window, у стилі Cloudflare): під час підрахунку поточної хвилини частково враховується частка хітів попередньої хвилини, тому різкий сплеск рівно на межі хвилини не дає змоги обійти ліміт подвоєною кількістю запитів.

Три заголовки присутні на кожній відповіді /api/v1 — і на успішних, і на помилках:

Заголовок Значення
X-RateLimit-Limit Ліміт ключа, запитів/хв.
X-RateLimit-Remaining Скільки запитів залишилося в поточному вікні.
X-RateLimit-Reset Unix-час (секунди), коли поточне вікно закінчиться і ліміт обнулиться.

За перевищення ліміту відповідь — 429 rate_limited з додатковим заголовком Retry-After (секунди до наступної спроби):

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1784625300
Retry-After: 42
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded.",
    "docs_url": "https://smengo.com/api-docs/reference/errors#rate_limited",
    "request_id": "8f14e45f-9e05-4f1a-8a3e-3d2b1c0f9b1b"
  }
}

Докладніше про код помилки — в Помилках → rate_limited.

Запити, відхилені на етапі автентифікації або перевірки скоупу (401 unauthorized, 403 missing_scope, 403 subscription_inactive), ліміт не витрачають — рахуються лише запити, що дійшли до самого лімітера.

Дати та час

  • Поля-дні (entry_date, date, period, from, to) — формат YYYY-MM-DD, у таймзоні організації, не в UTC.
  • Інстанти (check_in_at, created_at, updated_at, …) — ISO 8601 UTC із суфіксом Z: 2026-07-01T09:00:00.000Z.
  • Таймзона організації доступна у відповіді GET /org (поле timezone).

Фільтри

Застосовується до всіх часових ресурсів: schedule-entries, check-ins, timesheets, demand-signals.

  • from, toYYYY-MM-DD, у таймзоні організації, межі включно. from не може бути пізніше за to; максимальний діапазон — 366 днів; порушення будь-якої умови — 400 validation_error із поясненням у hint. Якщо параметри не передані — за замовчуванням використовується поточний місяць у таймзоні організації, тому запит взагалі без параметрів уже повертає змістовні дані. Для timesheets from/to зіставляються за period — першим днем місяця, якому належить табельний запис.
  • department_id — доступний скрізь, де в ресурсу є відділ (зокрема на employees); фільтр — строга рівність.
  • employee_id — доступний на schedule-entries, check-ins, timesheets.
  • Невідомий department_id/employee_id, зокрема той, що належить чужій організації, — не помилка: просто порожній список. Так тенант-ізоляція не дає змоги перебором перевірити існування чужих ідентифікаторів.
  • from/to на ресурсі без часового виміру (org, departments, employees, shift-presets, status-types) — 400 validation_error: зайвий параметр не ігнорується мовчки, а відхиляється явно.

Що далі

  • Помилки — формат envelope і повний каталог кодів.
  • Скоупи та права — яке право потрібне, щоб надати ключу доступ до кожного ресурсу.
Чи була стаття корисною?