For developers

The Floowa API

A REST API with JSON, personal API tokens and signed webhooks. The reference is public; to try it out you need a Floowa account.

Authentication

An administrator creates a token under Settings → API access, chooses the rights per function and an expiry date (30, 90 or 365 days or unlimited). The token is shown only once.

Authorization: Bearer <token>
Accept: application/json

Base URL: https://www.floowa.de/api/v1

  • Every request runs in the token owner's company and can never do more than the token and the owner's own rights allow.
  • The API is included in all plans; endpoints of booked modules are available as soon as the module is active.
  • Error messages come in the language of the Accept-Language header (English otherwise).

Limits and behaviour

Limits and behaviour
Rate limit 120 requests per minute and token; beyond that the API answers with HTTP 429.
Lists Lists are paginated; with per_page you get up to 100 entries per page.
Safe retries With the Idempotency-Key header a repeated request returns the same response for 24 hours instead of creating something twice.
API revision Every response carries the Floowa-Api-Revision header with the date of the last API change.

Webhooks and signature

Every delivery is a POST with JSON to your HTTPS address, with the headers X-Floowa-Event, X-Floowa-Delivery and X-Floowa-Signature.

The signature has the form t=<timestamp>,v1=<signature>. v1 is an HMAC-SHA256 over “<timestamp>.<raw body>” with your signing secret. Compare in constant time and reject old timestamps.

If your server does not answer with a 2xx status, Floowa retries after 1, 5 and 30 minutes and after 2 hours. After 20 failed attempts in a row the webhook is deactivated.

Example delivery

{
    "id": "8f2c3a1e-5b7d-4c1a-9e2f-0a1b2c3d4e5f",
    "event": "absence.requested",
    "created_at": "2026-10-28T09:15:00+01:00",
    "data": {
        "id": "01a1…",
        "employee_id": "01a0…",
        "absence_type_id": "01a0…",
        "start_date": "2026-11-02",
        "end_date": "2026-11-06",
        "half_day_start": false,
        "half_day_end": false,
        "days": 5,
        "status": "pending"
    }
}

Verify the signature (PHP)

$header = $_SERVER["HTTP_X_FLOOWA_SIGNATURE"] ?? "";
parse_str(str_replace(",", "&", $header), $parts);
$body = file_get_contents("php://input");
$expected = hash_hmac("sha256", $parts["t"] . "." . $body, $secret);

if (! hash_equals($expected, $parts["v1"] ?? "") || abs(time() - (int) $parts["t"]) > 300) {
    http_response_code(400);
    exit;
}

All events in plain words

Events Floowa sends via webhook
Event When Technical name
Employee created An employee is created – manually, by import or via the API. employee.created
Employee updated An employee's master data changes. employee.updated
Employee deleted An employee is deleted. employee.deleted
Absence requested Someone requests leave or another absence. absence.requested
Absence approved A request is approved or an absence is entered directly. absence.approved
Absence rejected A request is rejected. absence.rejected
Absence cancelled A request or an absence is withdrawn. absence.cancelled
Time recorded A time entry is created – when clocking in, when adding it later or at the terminal. time_entry.created
Time changed A time entry changes, for example when clocking out. time_entry.updated
Time deleted A time entry is deleted. time_entry.deleted
Month closed A month is closed in payroll preparation. payroll.closed
Month reopened A closed month is reopened for corrections. payroll.reopened
Shift published A shift is published with the shift plan. shift.published
Shift changed A published shift changes. shift.changed
Shift removed A published shift is deleted. shift.deleted

Changelog

All changes to the API with date and a note on breaking changes. Machine-readable at GET /api/v1/changelog.

2026-11-02 · November 2, 2026

  • Added Target hour settings on employees: responses include effective_weekly_hours (own weekly hours or the company default; null without fixed target hours), no_fixed_hours (no fixed target hours, e.g. managing directors or trust-based working time) and working_time_law_exempt (no working time act checks, e.g. managing directors of a GmbH or executive staff). POST and PUT accept no_fixed_hours and working_time_law_exempt as optional fields; new employment_type managing_director. weekly_hours is unchanged. GET /employeesGET /employees/{id}POST /employeesPUT /employees/{id}

2026-10-30 · October 30, 2026

  • Added Inventory inspections and qualifications required by inventory: GET /qualifications returns required_inventory_category_ids and the new category check (no document); GET /employees/{id}/qualifications returns requirement_reasons (why a qualification is required – unit or issued item); GET /inventory-items/{id} includes inspections with due_on, last_done_on and status (ok, due_soon, overdue). GET /qualificationsGET /employees/{id}/qualificationsGET /inventory-items/{id}
  • Added Absences: POST /absences/{id}/cancel accepts an optional reason and also cancels approved absences; responses include cancel_requested_at (an employee's open cancellation request) and cancel_reason. POST /absences/{id}/decision decides an open cancellation request (approve = cancel, reject = keep the absence). POST /absences/{id}/cancelGET /absences
  • Changed Project names only need to be unique per customer; the same name is allowed for different customers. Projects without a customer stay unique among themselves. POST /time-projectsPUT /time-projects/{id}

2026-10-29 · October 29, 2026

  • Added Project time per entry: time entries accept customer_id (customer without project) and parts (split across several projects/customers with minutes or start/end); GET /time-allocations returns customer_id, label, started_at/ended_at and kind part, filter customer_id. POST /time-entriesPUT /time-entries/{id}GET /time-allocations
  • Added Org units: clock_web and clock_app (true/false, null = as the parent) decide whether employees of the unit may clock in the browser or the app. GET /org-unitsPOST /org-unitsPUT /org-units/{id}
  • Removed breaking The free day assignment PUT /employees/{id}/time-allocations/{date} has been removed – please send the split as parts on the time entry. Existing free assignments were attached to the longest entry of their day. PUT /employees/{id}/time-allocations/{date}

2026-10-28 · October 28, 2026

  • Added Project time separate from attendance: GET /time-allocations returns which project a day's time is assigned to; PUT /employees/{id}/time-allocations/{date} sets a day's free assignments. time_project_id on time entries stays and creates a linked allocation automatically. GET /time-allocationsPUT /employees/{id}/time-allocations/{date}

2026-10-26 · October 26, 2026

  • Added Payroll preparation: new object surcharges_3b per employee with surcharge minutes under § 3b EStG (night_25, night_40, sunday_50, holiday_125, holiday_150). The fields night and holiday remain as totals. GET /payroll/{month}
  • Changed breaking Payroll preparation: sunday no longer counts public holidays, but includes 0–4 am on Monday after a shift started on Sunday. holiday additionally includes 24 and 31 December from 2 pm. Breaks are deducted proportionally. GET /payroll/{month}
  • Privacy/security breaking Absences: users with read rights only see confidential absence types (e.g. sick leave) as a neutral absence – absence_type_id, type, note and decision_note are null and the new field type_hidden is true. The payroll report groups such days in the column type_hidden. GET /absencesGET /reports/payroll
  • Added New endpoint GET /changelog and header Floowa-Api-Revision on every response. GET /changelog

2026-10-23 · October 23, 2026

  • Added First version of API v1: organisation, staff, documents, qualifications, inventory, salary statements, time tracking incl. clock and approval, projects and customers, absences, public holidays, reports, payroll preparation, Idempotency-Key and webhooks.

Let's get started. Welcome to Floowa.