Конвенции

Эта страница описывает конвенции, общие для всех эндпоинтов /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 и полный каталог кодов.
  • Скоупы и права — какое право нужно, чтобы выдать ключу доступ к каждому ресурсу.
Была ли статья полезна?