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

# Webhook-Integration

> Konfiguriere Webhooks in absentify für Ereignisse zu Abwesenheitsanfragen und Benutzern.

Bei einem abonnierten Ereignis sendet absentify einen HTTP-POST an deine URL.

## Netzwerkanforderungen

Wenn dein Endpunkt hinter einer Firewall liegt, erlaube die ausgehende IP-Adresse `4.231.227.89`.

## Webhooks konfigurieren

1. Öffne als Administrator **Einstellungen > Integrationen**.
2. Wähle **URL konfigurieren** und gib den Zielendpunkt ein.
3. Wähle einen Ereignistyp: **Alle Anfragen**, **Anfrage erstellt**, **Anfragestatus geändert**, **Benutzer erstellt**, **Benutzer aktualisiert** oder **Alle Benutzer**.
4. Wähle **Hinzufügen**.

## Payload-Versionen

Webhooks aus der Oberfläche verwenden aktuell das v1-Format. Ein Versionswähler ist noch nicht verfügbar.

| Version | Beschreibung                                    | Status  |
| ------- | ----------------------------------------------- | ------- |
| v1      | Aktuelles Format aller konfigurierten Webhooks  | Aktiv   |
| v2      | Public-API-v1-Antwort in `{ event_type, body }` | Geplant |

## Ereignistypen

| Wert                     | Bedeutung                     |
| ------------------------ | ----------------------------- |
| `request_created`        | Abwesenheitsanfrage erstellt  |
| `request_status_changed` | Status einer Anfrage geändert |
| `user_created`           | Benutzer erstellt             |
| `user_updated`           | Benutzer aktualisiert         |

## v1-Payloads

Anfrageereignisse senden ein flaches Objekt. `event_type` steht neben den Anfragedaten; es gibt keinen `body`-Wrapper.

```json theme={null}
{
  "event_type": "request_created",
  "id": "string",
  "request_creator_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
  "createdAt": "2026-01-15T10:00:00.000Z",
  "updatedAt": "2026-01-15T10:00:00.000Z",
  "start": "2026-01-20T00:00:00.000Z",
  "end": "2026-01-22T00:00:00.000Z",
  "start_at": "morning | afternoon",
  "end_at": "lunchtime | end_of_day",
  "leave_unit": "days | half_days | hours | minutes_30 | minutes_15 | minutes_10 | minutes_5 | minutes_1",
  "year": 2026,
  "duration": 3,
  "details": {
    "status": "PENDING | APPROVED | DECLINED | CANCELED",
    "deducted": 3,
    "allowance_delta": 3,
    "reason": "string | null",
    "approval_process": "string",
    "cancel_reason": "string | null",
    "canceld_date": "2026-01-15T10:00:00.000Z | null",
    "canceld_by_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
    "approver_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
    "approved_date": "2026-01-15T10:00:00.000Z | null",
    "decline_reason": "string | null",
    "declined_by_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
    "declined_date": "2026-01-15T10:00:00.000Z | null",
    "request_approvers": [
      {
        "id": "string",
        "status": "PENDING | APPROVED | DECLINED",
        "approver_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
        "reason": "string | null",
        "status_change_date": "2026-01-15T10:00:00.000Z | null",
        "predecessor_request_approver_id": "string | null"
      }
    ],
    "request_representative_approvers": [
      {
        "id": "string",
        "status": "PENDING | APPROVED | DECLINED | CANCELED | REPLACED",
        "approver_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
        "replaced_to_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" },
        "reason": "string | null",
        "status_change_date": "2026-01-15T10:00:00.000Z | null",
        "predecessor_request_approver_id": "string | null"
      }
    ],
    "requester_member": {
      "id": "string",
      "name": "string",
      "email": "string",
      "custom_id": "string | null",
      "allowances": [
        {
          "allowance_type_id": "string",
          "allowance": 25,
          "brought_forward": 5,
          "compensatory_time_off": 0,
          "remaining": 18,
          "taken": 7,
          "year": 2026
        }
      ]
    },
    "leave_type": {
      "id": "string",
      "name": "string",
      "take_from_allowance": true,
      "leave_unit": "days | half_days | hours | minutes_30 | minutes_15 | minutes_10 | minutes_5 | minutes_1",
      "allowance_type": { "id": "string", "name": "string", "ignore_allowance_limit": false, "allowance_unit": "days | hours" }
    }
  }
}
```

| Feld                                                 | Bedeutung                                                                                         |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `event_type`                                         | Unterscheidet Erstellung und Statusänderung.                                                      |
| `leave_unit`                                         | Auf der Anfrage gespeicherte Einheit; bestimmt `duration` und `details.deducted`.                 |
| `details.leave_type.leave_unit`                      | Aktuelle Einheit der Abwesenheitsart für neue Anfragen; kann bei historischen Anfragen abweichen. |
| `details.status`                                     | Aktueller Status der Anfrage.                                                                     |
| `canceld_date`, `cancel_reason`, `canceld_by_member` | Werte einer stornierten Anfrage. Die Schreibweise `canceld_*` entspricht der Payload.             |
| `details.allowance_delta`                            | Positiv, solange die Anfrage Kontingent verbraucht; nach Stornierung oder Ablehnung negativ.      |
| `details.requester_member.allowances[]`              | Wirksamer Jahreswert, Verbrauch und verbleibendes Kontingent je Kontingentart.                    |
| `details.request_representative_approvers`           | Vertreter im Genehmigungsablauf.                                                                  |

Benutzerereignisse verwenden einen Wrapper:

```json theme={null}
{
  "event_type": "user_updated",
  "body": {
    "id": "string",
    "email": "string | null",
    "name": "string | null",
    "custom_id": "string | null",
    "created_at": "2026-01-15T10:00:00.000Z",
    "updated_at": "2026-01-15T10:00:00.000Z",
    "status": "ACTIVE | INACTIVE",
    "is_admin": false,
    "has_billing_access": false,
    "employment_start_date": "2026-01-01T00:00:00.000Z | null",
    "employment_end_date": "2026-12-31T00:00:00.000Z | null",
    "birthday": "1990-05-15T00:00:00.000Z | null",
    "public_holiday": { "id": "string", "name": "string" },
    "departments": [{ "id": "string", "name": "string" }]
  }
}
```

Kontingentwerte in Webhooks sind wirksame Werte aus Regel plus manuellen Anpassungen und stimmen mit der REST API überein. Namen sind gespeicherte Namen; Webhooks akzeptieren kein `locale`.

## Geplantes v2-Format

v2 soll Anfrage- und Benutzerereignisse einheitlich verpacken:

```json theme={null}
{
  "event_type": "request_created | request_status_changed | user_created | user_updated",
  "body": {}
}
```

Bei Anfrageereignissen entspricht `body` dann `GET /api/v1/requests/{id}`, bei Benutzerereignissen `GET /api/v1/members/{id}`. v2 ist noch nicht in der Oberfläche verfügbar. Verwende bis zur Veröffentlichung das oben dokumentierte v1-Format.

## Zustellungsverlauf

Das **Benachrichtigungsprotokoll** in den Einstellungen zeigt Ereignistyp und Ergebnis jeder Zustellung. Mit **Erneut versuchen** sendest du eine fehlgeschlagene Zustellung erneut.
