Uptime probe. No authentication.
Response 200
{
"success": true,
"status": "ok",
"version": "v1",
"time": "2026-08-08T14:23:00Z"
}
Public REST API
A stateless, API-key-authenticated REST API for third-party integrations. Read your company's employees, locations, and performance data. Every response is JSON.
All endpoints are versioned under /v1. Every request must be made over HTTPS in production.
HTTP Basic Auth over HTTPS. Send an Authorization header with your key ID and secret, base64-encoded:
Authorization: Basic base64(KEY_ID:SECRET)
| Credential | Format | Notes |
|---|---|---|
KEY_ID | TK_PUB_LIVE_… (28) | Public identifier, uppercase, 16-hex tail. Safe to log. |
SECRET | TK_SEC_LIVE_… (60) | Private. Shown exactly once at creation; only a SHA-256 hash is stored — unrecoverable if lost. |
Auth failure returns 401 unauthorized. The API never distinguishes a bad key from a bad secret, to prevent enumeration.
curl -u TK_PUB_LIVE_A1B2C3D4E5F60718:TK_SEC_LIVE_… \
https://api.tikklok.app/v1/me
| Scope | Limit |
|---|---|
| Per key | 60 requests / minute · 1000 / hour (fixed windows) |
| Per IP, pre-auth | 20 requests / minute (before Basic Auth is checked) |
| Per IP, auth failures | 10 failures / minute → temporary lockout |
Exceeding a limit returns 429 rate_limited. Wait a minute and retry.
Every response is JSON with a success boolean. Errors also carry a short machine-readable error string. Reads never emit success: false for missing data — a 404 body means the resource itself was missing. Every response carries Cache-Control: no-store.
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_role | Unknown role filter value. |
| 400 | invalid_date | Missing/malformed date, or min_date > max_date. |
| 400 | range_too_large | Performance span exceeds 366 days. |
| 401 | unauthorized | Missing or invalid credentials. |
| 404 | not_found | Resource missing — or belongs to another company (indistinguishable, by design). |
| 413 | payload_too_large | Request body over 1 MB. |
| 429 | rate_limited | Rate limit exceeded. |
Every request is scoped to the company that owns the API key. Resources are addressed by their global IDs:
| ID | Meaning |
|---|---|
company_user_id | A person in your company (the "employee" identity). Stable, globally unique. |
location_id | One of your company's locations. Stable, globally unique. |
locations[] includes its location_user_id — the internal per-assignment key — used to target clock events for that employee at that location. Path parameters still address an assignment by the (company_user_id, location_id) pair. Any ID in a path that doesn't belong to your company returns 404 not_found — identical to a non-existent ID, so foreign IDs can't be probed. Request bodies must be ≤ 1 MB (413 payload_too_large otherwise).All endpoints are GET. Every endpoint except /v1/health requires authentication.
Uptime probe. No authentication.
{
"success": true,
"status": "ok",
"version": "v1",
"time": "2026-08-08T14:23:00Z"
}
Confirms your credentials work and shows which company they map to.
{
"success": true,
"company_id": 1,
"company_name": "Acme Corp",
"key_id": "TK_PUB_LIVE_A1B2C3D4E5F60718",
"key_name": "My Slack integration"
}
Paginated roster of your company's people (company_users), each with a lean list of the locations they're actively assigned to. Owners/admins appear too, with an empty locations array if they have no assignments.
| Param | Default | Notes |
|---|---|---|
limit | 100 | Clamped to 1–500. |
offset | 0 | For paging. |
location_id | — | Only employees actively assigned to this location. |
role | — | Company role: owner, admin, employee. Invalid → 400 invalid_role. |
include_inactive | 0 | 1 to include deactivated employees. |
{
"success": true,
"total": 25,
"limit": 100,
"offset": 0,
"data": [
{
"company_user_id": 42,
"first_name": "Jane",
"last_name": "Doe",
"is_active": true,
"locations": [
{ "location_user_id": 15, "location_id": 7, "location_name": "Main Store" }
]
}
]
}
Full detail for one employee plus every location assignment (active and historical — each carries its own is_active). Each assignment includes its location_user_id (the per-assignment key, used for API clock events). 404 not_found if the employee isn't in your company.
{
"success": true,
"data": {
"company_user_id": 42,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"tel": "+32 470 00 00 00",
"role": "employee",
"contract": "fixed_schedule",
"status": "active",
"is_active": true,
"created": "2025-01-15 10:30:00",
"updated": "2025-08-27 14:22:15",
"locations": [
{
"location_user_id": 15,
"location_id": 7,
"location_name": "Main Store",
"role": "staff",
"function_id": 3,
"function_name": "Sales",
"is_user_primary": true,
"clocking_allowed": true,
"overtime": true,
"valid_from": null,
"valid_to": null,
"is_active": true
}
]
}
}
One employee's assignment at one location. 404 not_found if the employee isn't in your company, the location isn't in your company, or there's no assignment linking the two.
{
"success": true,
"data": {
"company_user_id": 42,
"location_user_id": 15,
"location_id": 7,
"location_name": "Main Store",
"role": "staff",
"function_id": 3,
"function_name": "Sales",
"is_user_primary": true,
"clocking_allowed": true,
"overtime": true,
"valid_from": null,
"valid_to": null,
"is_active": true,
"created": "2025-01-15 10:30:00",
"updated": "2025-08-27 14:22:15"
}
}
The same performance data as the dashboard's downloadable report, as JSON, grouped by location. For each location the employee is (or was) assigned to, a per-day breakdown plus summed totals. All figures are hours (2 decimals); overtime_hours can be negative (undertime).
expected_hours = planned − closed − location_holiday − user_holiday − sick · overtime_hours = worked − expected
| Param | Notes |
|---|---|
min_date | YYYY-MM-DD |
max_date | YYYY-MM-DD, ≥ min_date |
Bad/missing date or min_date > max_date → 400 invalid_date. A span over 366 days → 400 range_too_large. 404 not_found if the employee isn't in your company. An employee with no data in range returns 200 with an empty locations array.
{
"success": true,
"data": {
"company_user_id": 42,
"employee_name": "Jane Doe",
"period": { "min_date": "2025-01-01", "max_date": "2025-01-31" },
"locations": [
{
"location_id": 7,
"location_name": "Main Store",
"days": [
{
"date": "2025-01-06",
"planned_hours": 8,
"worked_hours": 7.75,
"closed_hours": 0,
"location_holiday_hours": 0,
"user_holiday_hours": 0,
"sick_hours": 0,
"expected_hours": 8,
"overtime_hours": -0.25
}
],
"totals": {
"planned_hours": 40, "worked_hours": 39, "closed_hours": 0,
"location_holiday_hours": 0, "user_holiday_hours": 0, "sick_hours": 0,
"expected_hours": 40, "overtime_hours": -1
}
}
]
}
}
Your company's locations. qr_base64 is intentionally omitted (large, and clocking is handled app-side).
| Param | Default | Notes |
|---|---|---|
include_inactive | 0 | 1 to include deactivated locations. |
{
"success": true,
"data": [
{
"location_id": 7,
"name": "Main Store",
"street": "Rue de la Paix",
"house_nr": "42",
"postal_code": "1000",
"city": "Brussels",
"country": "BE",
"timezone": "Europe/Brussels",
"is_company_primary": true,
"latitude": 50.8503,
"longitude": 4.3517,
"geo_radius_meters": 100,
"is_active": true,
"created": "2024-06-01 09:00:00",
"updated": "2025-08-27 14:22:15"
}
]
}
One location, same object shape as the list. 404 not_found if it isn't in your company.
API keys are created, listed, and revoked from the Tikklok dashboard (Settings → API keys, owner/admin only). The secret is shown once at creation — copy it immediately. Revoking a key is immediate.
A machine-readable OpenAPI 3.0 description is available for import into Postman, Insomnia, or client-SDK generators.