> ## Documentation Index
> Fetch the complete documentation index at: https://platform.doodocs.kz/llms.txt
> Use this file to discover all available pages before exploring further.

# Реагировать на приём и увольнение

> Обрабатывать вебхуки о найме и увольнении сотрудников

Вместо периодического опроса подпишитесь на события и обновляйте свою копию, как
только сотрудника завели или уволили в ТОО «Керуен». Событие лишь сообщает, что
именно изменилось, — актуальные данные вы забираете своим ключом.

<Note>
  Подписками управляет область `webhooks:manage`, а перечитывание карточек требует
  `employees:read`.
</Note>

<Steps>
  <Step title="Подпишитесь на события">
    Заведите endpoint на приём и удаление сотрудников. `secret` в ответе
    показывается **один раз** — сохраните его в менеджере секретов, им проверяется
    подпись каждой доставки.

    ```bash theme={null}
    curl -X POST -H "X-API-Key: ddp_ВАШ_КЛЮЧ" -H "Content-Type: application/json" \
      https://app.doodocs.kz/api/developer/v1/webhook_endpoints \
      -d '{
        "url": "https://keruen.example/hooks/doodocs",
        "event_types": ["employee.changed", "employee.deleted"]
      }'
    ```

    <Note>
      `employee.changed` объединяет создание и изменение: отдельного `created` нет
      намеренно. Обрабатывайте событие как upsert — источник не всегда может
      надёжно отличить первое появление сотрудника от последующего изменения.
    </Note>
  </Step>

  <Step title="Проверьте подпись">
    Каждая доставка несёт заголовок `Doodocs-Signature: t=<unix>,v1=<hex>`, где
    `v1` — это `HMAC-SHA256(secret, "{t}.{тело запроса}")` в hex. Отклоните запрос,
    если подпись не сходится или `t` старше пяти минут (защита от повторов).

    ```python theme={null}
    import hashlib, hmac, time

    def verify(secret: str, header: str, body: bytes) -> bool:
        parts = dict(p.split("=", 1) for p in header.split(","))
        t, v1 = parts["t"], parts["v1"]
        if abs(time.time() - int(t)) > 300:
            return False
        expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
        return hmac.compare_digest(expected, v1)
    ```
  </Step>

  <Step title="Перечитайте и примените upsert">
    В payload приходит только `employee_id`. Забирайте карточку своим ключом — так
    к данным применяются область ключа и редакция полей, а персональные данные не
    уезжают на внешний URL.

    ```bash theme={null}
    curl -H "X-API-Key: ddp_ВАШ_КЛЮЧ" \
      "https://app.doodocs.kz/api/developer/v1/employees/3f2a9c7e-…?expand=person&expand=department"
    ```

    * `employee.changed` → перечитайте `GET /employees/{employee_id}` и сделайте
      upsert по `id`.
    * `employee.deleted` → перечитывание вернёт `404` `EMPLOYEE_NOT_FOUND`. Не
      считайте это ошибкой: событие самодостаточно, пометьте сотрудника удалённым.
  </Step>
</Steps>

## Обработчик

Дедуплицируйте по `id` события (доставка «хотя бы один раз»), ответьте `2xx`
быстро, а тяжёлую работу выносите в фон.

```python theme={null}
import json, requests

BASE = "https://app.doodocs.kz/api/developer/v1"
HEADERS = {"X-API-Key": "ddp_ВАШ_КЛЮЧ"}
SECRET = "…"                                   # сохранённый secret подписки

def handle(headers, body: bytes):
    if not verify(SECRET, headers["Doodocs-Signature"], body):
        return 401
    event = json.loads(body)
    if seen(event["id"]):                     # дедупликация по id события
        return 200

    employee_id = event["data"]["employee_id"]
    if event["type"] == "employee.deleted":
        mark_deleted(employee_id)
        return 200

    resp = requests.get(f"{BASE}/employees/{employee_id}", headers=HEADERS,
                        params={"expand": ["person", "department"]})
    if resp.status_code == 404:               # гонка: удалён между событием и перечитом
        mark_deleted(employee_id)
    else:
        upsert(resp.json())                   # employee.changed = create + update
    return 200
```

<Warning>
  Между `employee.changed` и вашим перечитом сотрудника могут удалить — тогда
  `GET /employees/{id}` вернёт `404`. Трактуйте это как удаление, а не как сбой.
</Warning>

## Дальше

<Columns cols={2}>
  <Card title="Синхронизировать всех сотрудников" icon="users" href="/ru/guides/recipes/sync-employees">
    Первичная выгрузка и полный перечит для сверки удалений.
  </Card>

  <Card title="Вебхуки" icon="bolt" href="/ru/api-reference/webhooks">
    Все типы событий, надёжность доставки и отладка.
  </Card>
</Columns>
