> ## 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

> Configure webhooks in absentify to receive HTTP POST notifications for absence requests, approvals, user changes, and other events in real time.

Webhooks in absentify allow you to call a script on your server whenever specific events occur in the absentify system. Acting as event listeners or push notifications, webhooks let you build integrations that respond to certain events, such as creating or updating requests, or managing users. When a subscribed event is triggered, absentify sends an HTTP POST request with relevant data to the specified webhook URL. This enables you to update external systems, send alerts, or automate actions based on changes in absentify.

## Network requirements

If your webhook endpoint is behind a firewall, allowlist the following IP address so absentify can send webhook notifications:

**Outbound IP address:** `4.231.227.89`

All webhook HTTP POST requests from absentify originate from this IP address.

## Configuring webhooks

To set up webhooks, go to the **Settings** page in absentify.

1. **Sign in and navigate to integrations**
   Sign in as an administrator, then select **Settings** in the header, and select **Integrations**.

2. **Set the webhook URL**
   Select **Configure URL** and enter the URL where you want to receive event data.

3. **Select event type**
   From the dropdown menu, select the event type you want to subscribe to:

   * All requests (`request_created` and `request_status_changed`)
   * Request Created
   * Request Status Changed
   * User Created
   * User Updated
   * All users (`user_created` and `user_updated`)

4. **Save the configuration**
   Select **Add** to complete the integration and start receiving notifications for the selected event types.

***

### Payload format

Webhooks configured through the absentify UI currently use the **v1 payload format**. This is the default for all new and existing webhook endpoints.

| Version | Description                                                             | Status     |
| ------- | ----------------------------------------------------------------------- | ---------- |
| v1      | Current format used by all UI-configured webhooks                       | **Active** |
| v2      | Identical to Public API v1 responses, wrapped in `{ event_type, body }` | Planned    |

<Info>
  A payload version selector is not yet available in the webhook settings UI. All webhooks created through the UI receive the v1 format described below.
</Info>

***

### Event types

| Event                    | Description                                                      |
| ------------------------ | ---------------------------------------------------------------- |
| `request_created`        | New absence request created                                      |
| `request_status_changed` | Status of a request changed (approved, declined, canceled, etc.) |
| `user_created`           | New user created                                                 |
| `user_updated`           | User updated                                                     |

The `event_type` field in the payload reflects the event that triggered the webhook delivery.

***

### Webhook payload structure (v1 — current)

Request events and user events use **different wrapper structures** in v1.

#### Request events (`request_created`, `request_status_changed`)

Request webhooks send a **flat JSON object** at the root. The `event_type` field is included alongside the request data — there is no separate `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": "2024-01-15T10:00:00.000Z",
  "updatedAt": "2024-01-15T10:00:00.000Z",
  "start": "2024-01-20T00:00:00.000Z",
  "end": "2024-01-22T00:00:00.000Z",
  "start_at": "morning | afternoon",
  "end_at": "lunchtime | end_of_day",
  "leave_unit": "days | hours",
  "year": 2024,
  "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": "2024-01-15T10:00:00.000Z | null",
    "canceld_by_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" } | null,
    "approver_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" } | null,
    "approved_date": "2024-01-15T10:00:00.000Z | null",
    "decline_reason": "string | null",
    "declined_by_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" } | null,
    "declined_date": "2024-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": "2024-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" } | null,
        "replaced_to_member": { "id": "string", "name": "string", "email": "string", "custom_id": "string | null" } | null,
        "reason": "string | null",
        "status_change_date": "2024-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": 2024
        }
      ]
    },
    "leave_type": {
      "id": "string",
      "name": "string",
      "take_from_allowance": true,
      "leave_unit": "days | hours",
      "allowance_type": { "id": "string", "name": "string", "ignore_allowance_limit": false, "allowance_unit": "days | hours" }
    }
  }
}
```

**Key fields for event evaluation:**

| Field                                                     | Use                                                                                                                                                                                                   |
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_type`                                              | Distinguishes `request_created` from `request_status_changed`                                                                                                                                         |
| `details.status`                                          | Current request status (`PENDING`, `APPROVED`, `DECLINED`, `CANCELED`)                                                                                                                                |
| `canceld_date`, `cancel_reason`, `canceld_by_member`      | Populated when a request is canceled                                                                                                                                                                  |
| `details.deducted`                                        | Absence duration of the request (always positive)                                                                                                                                                     |
| `details.allowance_delta`                                 | Signed allowance impact: **positive** while the request consumes allowance (e.g. created/approved), **negative** once it is canceled/declined and the days are returned. Same magnitude as `deducted` |
| `details.requester_member.allowances[].allowance_type_id` | Identifies which quota each allowance entry belongs to (map total `allowance` / used `taken` / available `remaining`)                                                                                 |
| `details.requester_member.allowances[].allowance`         | The **effective** allowance for the year: the amount granted by the employee's allowance rule plus all manual adjustments. This matches the `allowance` field returned by the REST API                |
| `details.request_representative_approvers`                | Representatives in the approval process (empty if none)                                                                                                                                               |
| `createdAt`, `updatedAt`                                  | Timestamps for the request record                                                                                                                                                                     |

#### User events (`user_created`, `user_updated`)

User webhooks send a **wrapped JSON object** with `event_type` and `body`:

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

<Note>
  Every allowance value sent by a webhook is the **effective** allowance: what the employee's [allowance rule](/docs/en/roles/hr_admins/settings/allowances#creating-and-managing-allowance-rules) granted for the year plus all manual adjustments. Webhooks and the REST API therefore always report the same number for the same employee and year.
</Note>

***

### Planned v2 format

v2 will align request webhook payloads with the Public API v1 response format and use a consistent wrapper for all event types:

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

For request events, the `body` will match `GET /api/v1/requests/{id}` (flat structure, no `details` wrapper). User event `body` will match `GET /api/v1/members/{id}`.

v2 will become available through the webhook settings UI in a future release. Until then, all integrations should use the v1 format documented above.

***

### Webhook history

The **Notification log** in absentify settings shows the correct event type for each delivery (`Request created`, `Request status changed`, etc.), independent of your endpoint's processing logic. Use the **Retry** button to resend failed deliveries.

***
