Publieke REST API

Tikklok REST API v1

Een stateless, met API-sleutel geauthenticeerde REST API voor integraties van derden. Lees de medewerkers, vestigingen en prestatiegegevens van je bedrijf. Elke respons is JSON.

Basis-URL

https://api.tikklok.appproductie
http://localhost:8084lokale ontwikkeling

Alle endpoints staan onder versie /v1. Elke aanvraag moet in productie via HTTPS gebeuren.

Authenticatie

HTTP Basic Auth via HTTPS. Stuur een Authorization-header met je sleutel-ID en secret, base64-gecodeerd:

Authorization: Basic base64(KEY_ID:SECRET)
CredentialFormaatOpmerkingen
KEY_IDTK_PUB_LIVE_… (28)Publieke identificator, hoofdletters, 16-hex staart. Veilig om te loggen.
SECRETTK_SEC_LIVE_… (60)Privé. Wordt exact één keer getoond bij aanmaak; alleen een SHA-256-hash wordt bewaard — onherstelbaar als je hem kwijt bent.

Bij een mislukte authenticatie volgt 401 unauthorized. De API maakt nooit onderscheid tussen een foute sleutel en een foute secret, om enumeratie te voorkomen.

Voorbeeld

curl -u TK_PUB_LIVE_A1B2C3D4E5F60718:TK_SEC_LIVE_… \
     https://api.tikklok.app/v1/me

Rate limits

BereikLimiet
Per sleutel60 aanvragen / minuut · 1000 / uur (vaste vensters)
Per IP, vóór auth20 aanvragen / minuut (voordat Basic Auth wordt gecontroleerd)
Per IP, mislukte auth10 mislukkingen / minuut → tijdelijke blokkade

Bij overschrijding volgt 429 rate_limited. Wacht een minuut en probeer opnieuw.

Responses & fouten

Elke respons is JSON met een success-boolean. Fouten dragen ook een korte machineleesbare error-string. Reads geven nooit success: false bij ontbrekende data — een 404-body betekent dat de resource zelf ontbrak. Elke respons draagt Cache-Control: no-store.

StatuserrorBetekenis
400invalid_roleOnbekende role-filterwaarde.
400invalid_dateOntbrekende/ongeldige datum, of min_date > max_date.
400range_too_largePrestatieperiode langer dan 366 dagen.
401unauthorizedOntbrekende of ongeldige gegevens.
404not_foundResource ontbreekt — of hoort bij een ander bedrijf (niet te onderscheiden, met opzet).
413payload_too_largeAanvraagbody groter dan 1 MB.
429rate_limitedRate limit overschreden.

Scope & ID's

Elke aanvraag is beperkt tot het bedrijf dat de API-sleutel bezit. Resources worden geadresseerd met hun globale ID's:

IDBetekenis
company_user_idEen persoon in je bedrijf (de „medewerker”-identiteit). Stabiel, globaal uniek.
location_idEén van de vestigingen van je bedrijf. Stabiel, globaal uniek.
De locations[] van elke medewerker bevat de location_user_id — de interne toewijzingssleutel — waarmee je klokgebeurtenissen voor die medewerker op die vestiging aanstuurt. Padparameters adresseren een toewijzing nog steeds met het paar (company_user_id, location_id). Een ID in een pad dat niet bij je bedrijf hoort, geeft 404 not_found — identiek aan een onbestaand ID, zodat vreemde ID's niet afgetast kunnen worden. Aanvraagbodies moeten ≤ 1 MB zijn (anders 413 payload_too_large).

Endpoints

Alle endpoints zijn GET. Elk endpoint behalve /v1/health vereist authenticatie.

GET/v1/healthgeen auth

Uptime-check. Geen authenticatie.

Respons 200

{
  "success": true,
  "status": "ok",
  "version": "v1",
  "time": "2026-08-08T14:23:00Z"
}
GET/v1/meauth

Bevestigt dat je gegevens werken en toont bij welk bedrijf ze horen.

Respons 200

{
  "success": true,
  "company_id": 1,
  "company_name": "Acme Corp",
  "key_id": "TK_PUB_LIVE_A1B2C3D4E5F60718",
  "key_name": "My Slack integration"
}
GET/v1/employeesauth

Gepagineerde lijst van de mensen in je bedrijf (company_users), elk met een beknopte lijst van de vestigingen waaraan ze actief zijn toegewezen. Owners/admins verschijnen ook, met een lege locations-array als ze geen toewijzingen hebben.

Queryparameters

ParamStandaardOpmerkingen
limit100Begrensd tot 1–500.
offset0Voor paginering.
location_idAlleen medewerkers die actief aan deze vestiging zijn toegewezen.
roleBedrijfsrol: owner, admin, employee. Ongeldig → 400 invalid_role.
include_inactive01 om gedeactiveerde medewerkers mee te nemen.

Respons 200

{
  "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" }
      ]
    }
  ]
}
GET/v1/employees/{company_user_id}auth

Volledige details van één medewerker plus elke vestigingstoewijzing (actief en historisch — elk met zijn eigen is_active). Elke toewijzing bevat zijn location_user_id (de toewijzingssleutel, voor klokgebeurtenissen via de API). 404 not_found als de medewerker niet in je bedrijf zit.

Respons 200

{
  "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
      }
    ]
  }
}
GET/v1/employees/{company_user_id}/locations/{location_id}auth

De toewijzing van één medewerker op één vestiging. 404 not_found als de medewerker niet in je bedrijf zit, de vestiging niet in je bedrijf zit, of er geen toewijzing tussen beide bestaat.

Respons 200

{
  "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"
  }
}
GET/v1/employees/{company_user_id}/performanceauth

Dezelfde prestatiegegevens als het downloadbare rapport van het dashboard, als JSON, gegroepeerd per vestiging. Voor elke vestiging waaraan de medewerker is (of was) toegewezen, een opsplitsing per dag plus opgetelde totals. Alle cijfers zijn uren (2 decimalen); overtime_hours kan negatief zijn (te weinig gewerkt).

expected_hours = planned − closed − location_holiday − user_holiday − sick · overtime_hours = worked − expected

Queryparameters (beide verplicht)

ParamOpmerkingen
min_dateYYYY-MM-DD
max_dateYYYY-MM-DD, ≥ min_date

Ongeldige/ontbrekende datum of min_date > max_date400 invalid_date. Een periode langer dan 366 dagen → 400 range_too_large. 404 not_found als de medewerker niet in je bedrijf zit. Een medewerker zonder data in de periode geeft 200 met een lege locations-array.

Respons 200

{
  "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
        }
      }
    ]
  }
}
GET/v1/locationsauth

De vestigingen van je bedrijf. qr_base64 wordt bewust weggelaten (groot, en inklokken gebeurt aan de app-kant).

Queryparameters

ParamStandaardOpmerkingen
include_inactive01 om gedeactiveerde vestigingen mee te nemen.

Respons 200

{
  "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"
    }
  ]
}
GET/v1/locations/{location_id}auth

Eén vestiging, dezelfde objectvorm als de lijst. 404 not_found als ze niet in je bedrijf zit.

API-sleutelbeheer

API-sleutels worden aangemaakt, opgelijst en ingetrokken vanuit het Tikklok-dashboard (Instellingen → API-sleutels, alleen owner/admin). De secret wordt één keer getoond bij aanmaak — kopieer hem meteen. Intrekken gebeurt onmiddellijk.

OpenAPI-spec

Een machineleesbare OpenAPI 3.0-beschrijving is beschikbaar om te importeren in Postman, Insomnia of generators voor client-SDK's.

openapi.yaml downloaden