Приход и уход сотрудников

Если в заведении время сотрудников отмечает своя система — мобильное приложение, турникет, СКУД, терминал на входе, — она может отправлять отметки в Mison через API.

Отметки ложатся туда же, куда отметки камеры Hikvision и кассы: их видно на странице «Посещаемость» в кабинете, по ним считается зарплата по часам. Для заведения нет разницы, откуда пришла смена.

Доступ

Раздел открыт только ключу с галочкой «Приход и уход сотрудников». Её включает заведение в кабинете: Интеграции → API-ключи → Редактировать. По умолчанию галочка выключена: отметки влияют на зарплату, поэтому ключ под заказы или отчёты писать их не может.

Без галочки любой метод раздела отвечает 403:

{ "message": "У ключа нет доступа к приходу и уходу сотрудников. Включите его в кабинете: Интеграции → API-ключи" }

Доступ к отчётам для этого раздела не нужен — это отдельная галочка.

Порядок работы

  1. Загрузите список сотрудников — GET /attendance/employees — и сопоставьте их со своими. В Mison сотрудник адресуется employee_id.
  2. На каждый приход и уход отправляйте событие — POST /attendance/events. Можно сразу, можно пачкой из офлайн-очереди.
  3. Для сверки читайте получившиеся смены — GET /attendance/shifts.

Вам не нужно самим собирать смены: присылайте отдельные события «пришёл» и «ушёл», а Mison сложит их в смены по тем же правилам, что и отметки камеры.

Сотрудники

GET /attendance/employees?point_id={uuid}&search=

Действующие сотрудники бренда — тех, кого можно отмечать. Заблокированные и удалённые сюда не попадают, их отметки не принимаются.

ПараметрОписание
point_idUUID точки, можно несколько через запятую. Без него — все точки ключа
searchПоиск по имени
{
  "count": 1,
  "employees": [
    {
      "employee_id": 3873,
      "name": "Абдулазиз Каримжонов",
      "position_id": 1186,
      "position_name": "Повар",
      "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
      "point_name": "Центральная",
      "open_shift": {
        "shift_id": 73412,
        "check_in": "2026-10-19 08:58:12"
      }
    }
  ]
}

open_shift — открытая смена: сотрудник сейчас на смене или забыл отметить уход. null — открытой смены нет. Удобно, чтобы показать в приложении кнопку «Пришёл» или «Ушёл».

point_id сотрудника — точка из его карточки в кабинете. Именно на неё ляжет отметка, если не указать точку в событии.

Отправка отметок

POST /attendance/events

В одном запросе — от 1 до 500 событий. Каждое разбирается отдельно: ошибка в одном не отменяет остальные.

curl -X POST https://backend.mison.uz/api/integrations/v1/attendance/events \
  -H "Authorization: Bearer 44595|M4sx2SHynVRIX9CUAS3..." \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_id": "door-1-000184",
        "employee_id": 3873,
        "type": "check_in",
        "time": "2026-10-19T08:58:12+05:00"
      },
      {
        "event_id": "door-1-000185",
        "employee_id": 3873,
        "type": "check_out",
        "time": "2026-10-19T18:04:40+05:00",
        "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388"
      }
    ]
  }'
ПолеОбязательноеОписание
event_idдаВаш идентификатор события, до 100 символов. По нему повтор не создаст дубль — см. повторная отправка
employee_idдаСотрудник из GET /attendance/employees
typeдаcheck_in — пришёл, check_out — ушёл
timeдаКогда это произошло на самом деле, а не когда вы отправляете запрос. Формат — ниже
point_idнетUUID точки, где сделана отметка. Без него — точка сотрудника из его карточки

Формат времени

  • ISO 8601 с часовым поясом — рекомендуем: 2026-10-19T08:58:12+05:00 или 2026-10-19T03:58:12Z. Mison переведёт его в местное время.
  • Без часового пояса — 2026-10-19 08:58:12 или 2026-10-19T08:58. Считается местным временем заведения (Ташкент, UTC+5).
  • Unix-время в секундах — 1792382292 (тот же момент, что в примерах выше).

Время не может быть в будущем (часам устройства прощается опережение до 5 минут) и старше 62 дней. Смену за более ранний период заведение вносит в кабинете.

Ответ

Ответ всегда 200, итог — по каждому событию в results, в том же порядке, что в запросе.

{
  "accepted": 2,
  "duplicates": 0,
  "rejected": 0,
  "results": [
    {
      "event_id": "door-1-000184",
      "status": "accepted",
      "result": "shift_opened",
      "shift_id": 73412,
      "auto_closed_shift_id": null,
      "message": null
    },
    {
      "event_id": "door-1-000185",
      "status": "accepted",
      "result": "shift_closed",
      "shift_id": 73412,
      "auto_closed_shift_id": null,
      "message": null
    }
  ]
}

400 приходит, только если сломан сам запрос: нет массива events или в нём больше 500 событий.

status — что стало с событием:

statusЧто значитЧто делать
acceptedСобытие принято. Что именно оно сделало — в resultУдалить из своей очереди
duplicateЭто event_id уже присылали. result и shift_id — от первой отправки, ничего не изменилосьУдалить из своей очереди
rejectedСобытие не принято, причина — в messageИсправить данные. Повтор того же события получит тот же отказ

result — что событие сделало со сменой:

resultЧто произошло
shift_openedОткрыта новая смена
shift_closedСмена закрыта этим уходом
check_out_without_shiftУход без прихода: открытой смены не было. Заведена смена с одним временем ухода — заведение увидит её в «Посещаемости» и поправит
shift_already_openПриход, когда сотрудник уже на смене. Ничего не изменилось, shift_id — открытая смена
already_countedТакая же отметка этого сотрудника в пределах 5 минут уже есть — с камеры, кассы или от вас под другим event_id. Ничего не изменилось
inside_shiftОтметка попала внутрь уже закрытой смены — запоздавшее событие. Часы этой смены уже посчитаны, ничего не изменилось

auto_closed_shift_id заполняется, когда приход закрыл висящую смену (см. правила ниже).

shift_id — идентификатор смены, тот же, что в GET /attendance/shifts.

Причины отказа (message при rejected):

messageПричина
event_id обязателенНет event_id или он пустой
event_id повторяется в запросеДва события с одним event_id в одном запросе — принято первое
type: check_in или check_outНеизвестный тип события
time: дата и время в формате ISO 8601…Время не разобрать
Время события в будущемЧасы устройства спешат больше чем на 5 минут
Событие старше 62 дней…Слишком старое событие — смену вносят в кабинете
Сотрудник не найденНет такого employee_id у бренда или сотрудник удалён
Сотрудник заблокированСотрудник уволен (заблокирован в кабинете)
Торговая точка не найдена: …point_id не принадлежит бренду ключа
У сотрудника не задана точка — передайте point_idВ карточке сотрудника нет точки, а в событии её не передали

Как отметки складываются в смены

Правила те же, что у кассы с камерой Hikvision, — смена из API и смена с камеры ведут себя одинаково. Отметки разных точек не смешиваются: приход на одной точке не закрывается уходом на другой.

Приход (check_in)

  • Нет открытой смены → открывается новая (shift_opened).
  • Сотрудник уже на смене, открытой меньше 14 часов назад, → ничего не меняется (shift_already_open). Повторное прикладывание карты не открывает вторую смену.
  • Смена открыта больше 14 часов назад → её уход потерялся. Она закрывается временем нового прихода с пометкой «Автозакрытие: выход не был зарегистрирован», и открывается новая (shift_opened, auto_closed_shift_id — закрытая смена).

Уход (check_out)

  • Закрывает смену, начатую до него (shift_closed). Смену ищем по времени события, а не «последнюю»: запоздавший уход не закроет смену, которая началась уже после него.
  • Открытой смены до этого времени нет → заводится смена с одним уходом и пометкой «Выход без открытой смены» (check_out_without_shift). В часы она не идёт, пока заведение не поправит её в кабинете.

Повторы. Та же отметка этого сотрудника в пределах 5 минут не учитывается второй раз (already_counted) — откуда бы ни пришла первая: от вас, с камеры или с кассы. Отметка внутри уже закрытой смены тоже ничего не меняет (inside_shift).

Повторная отправка

Присылайте у каждого события свой постоянный event_id — номер записи в журнале турникета, UUID, сгенерированный в приложении в момент отметки. Повтор с тем же event_id вернёт duplicate и результат первой отправки, а не откроет вторую смену. Поэтому запрос можно безопасно повторять при таймауте, обрыве сети, 429 или 500.

event_id уникален в пределах ключа. Если под уже использованным event_id прийти с другим событием (другой сотрудник, тип или время), оно не применится: вернётся duplicate с пометкой в message.

Офлайн-очередь. Если устройство работает без сети, копите события и отправляйте пачкой, когда связь вернётся. Внутри одного запроса Mison сам применяет события по времени time, а не по порядку в массиве — уход, оказавшийся в очереди раньше прихода, не сломает смену. Между запросами отправляйте события в порядке времени.

Смены

GET /attendance/shifts?date_from=2026-10-01&date_to=2026-10-19&point_id={uuid}

Смены сотрудников за период — все, а не только присланные вами: с камеры, с кассы и внесённые в кабинете. Это тот же ответ, что у отчёта /reports/attendance, но без доступа к остальным отчётам. Период — до 62 дней, point_id можно не передавать или передать несколько через запятую.

{
  "date_from": "2026-10-19",
  "date_to": "2026-10-19",
  "employees": [
    {
      "employee_id": 3873,
      "employee_login": "user3873",
      "employee_name": "Абдулазиз Каримжонов",
      "position_name": "Повар",
      "hours": 9.11,
      "open_shifts": 0,
      "worked_days": 1
    }
  ],
  "shifts": [
    {
      "shift_id": 73412,
      "employee_id": 3873,
      "employee_login": "user3873",
      "employee_name": "Абдулазиз Каримжонов",
      "position_name": "Повар",
      "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
      "point_name": "Центральная",
      "date": "2026-10-19",
      "check_in": "2026-10-19 08:58:12",
      "check_out": "2026-10-19 18:04:40",
      "hours": 9.11,
      "source": "api",
      "note": null
    }
  ]
}
ПолеОписание
shift_idИдентификатор смены — тот же, что в ответе POST /attendance/events
sourceОткуда смена: api — партнёрское API, cashbox — камера или касса, manual — внесена в кабинете
noteПометка: автозакрытие, уход без прихода и т. п.
check_in / check_outnull — отметки нет: сотрудник ещё на смене или отметка потерялась

Лимит — 60 запросов в минуту, как у отчётов.

Исправления

Через API смену можно только дополнить новыми отметками — поменять время или удалить смену нельзя. Ошибочную смену заведение правит или удаляет в кабинете на странице «Посещаемость», как смену с камеры. Если заморожена зарплата за прошлый месяц, а смена пришла задним числом, начисление пересчитается само.