A REST API with JSON, personal API tokens and signed webhooks. The reference is public; to try it out you need a Floowa account.
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
| 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. |
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.
{
"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"
}
}
$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;
}
| 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 |
All changes to the API with date and a note on breaking changes. Machine-readable at GET /api/v1/changelog.
GET /employeesGET /employees/{id}POST /employeesPUT /employees/{id}
GET /qualificationsGET /employees/{id}/qualificationsGET /inventory-items/{id}
POST /absences/{id}/cancelGET /absences
POST /time-projectsPUT /time-projects/{id}
POST /time-entriesPUT /time-entries/{id}GET /time-allocations
GET /org-unitsPOST /org-unitsPUT /org-units/{id}
PUT /employees/{id}/time-allocations/{date}
GET /time-allocationsPUT /employees/{id}/time-allocations/{date}
GET /payroll/{month}
GET /payroll/{month}
GET /absencesGET /reports/payroll
GET /changelog