Skip to content
ClockLap Developers
API reference clocklap.com Get an API key

Get started

API basics

Everything every request has in common, in one place: where to send it, what comes back, and how to handle errors and retries.

Regions and addresses#

RegionAPI addressKeys start with
Canadahttps://ca-api.clocklap.comtt_live_ca_, tt_test_ca_
United Stateshttps://us-api.clocklap.comtt_live_us_, tt_test_us_

Every path starts with /v1. Every response says which region answered in X-ClockLap-Region. Building something many workspaces will use? Find a workspace's region with GET /v1/directory/resolve?workspace=<slug> (no key needed), which returns its api_url.

Requests and responses#

  • Send JSON with Content-Type: application/json; you get JSON back. Field names are snake_case.
  • IDs are strings with a prefix that says what they are: usr_ person, loc_ location, ent_ time entry, evt_ event.
  • Lists come back as { "data": [...], "has_more": true, "next_cursor": "…" }.
  • Request bodies can be up to 2 MB; requests time out after 120 seconds.
  • Every response has an X-Request-Id (req_…). Include it when you contact us.

Dates and times#

  • Moments are ISO 8601 with an offset: 2026-10-26T08:02:00-04:00 or …Z. Inputs without an offset are refused.
  • Days are YYYY-MM-DD and times of day HH:MM, in the site's own time zone.
  • Time entries include their site's timezone and local_date, so you never have to work out which day a night shift belongs to.

Pagination#

List endpoints take limit (usually 1 to 500, default 100) and cursor. Keep passing next_cursor back until has_more is false. Cursors are opaque: don't build or change them. (The events API pages with after_id instead.)

async function* all(path, params = {}) {
  let cursor;
  do {
    const q = new URLSearchParams({ ...params, limit: '500', ...(cursor ? { cursor } : {}) });
    const res = await fetch(`https://ca-api.clocklap.com${path}?${q}`, { headers: { Authorization: `Bearer ${process.env.CLOCKLAP_API_KEY}` } });
    const page = await res.json();
    yield* page.data;
    cursor = page.has_more ? page.next_cursor : null;
  } while (cursor);
}

for await (const person of all('/v1/users', { status: 'active' })) console.log(person.name);

Errors#

Errors use the standard problem details format (application/problem+json). Read code in your program and show detail to people: it says what to fix in plain words.

{
  "type": "https://developer.clocklap.com/errors/validation_failed",
  "title": "Invalid request",
  "status": 400,
  "code": "validation_failed",
  "detail": "clock_in_at: Invalid ISO datetime",
  "request_id": "req_4f1c2a9b7d3e5f60"
}

The full list is on Errors.

Rate limits#

Each API key can make 600 requests a minute. Every response tells you where you stand:

HeaderMeaning
RateLimit-LimitRequests allowed in the window
RateLimit-RemainingRequests left
RateLimit-ResetSeconds until the window resets

Over the limit you get 429 rate_limited: wait for RateLimit-Reset seconds and try again. To read lots of data, use limit=500 and webhooks instead of polling.

Safe retries (idempotency)#

Networks fail. To retry a POST, PATCH, PUT or DELETE without doing it twice, send an Idempotency-Key header with a unique value (a UUID is ideal, up to 120 characters). ClockLap remembers the answer for 24 hours: a retry with the same key gets the same response, marked Idempotent-Replayed: true, and nothing happens twice.

curl -X POST https://ca-api.clocklap.com/v1/time-entries \
  -H "Authorization: Bearer $CLOCKLAP_API_KEY" \
  -H "Idempotency-Key: 6f1d9a2e-2b7c-4c55-9a51-0c8b8f1f7e21" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"usr_…","location_id":"loc_…","clock_in_at":"2026-10-26T08:00:00-04:00","clock_out_at":"2026-10-26T16:00:00-04:00","reason":"Forgot to clock in"}'
If…You get
the same key is sent againthe first response again, with Idempotent-Replayed: true
the first request is still running409 idempotency_key_busy: wait a moment and retry
the same key is used for a different request409 idempotency_key_reused: make a new key

Versions and changes#

The API is /v1 and changes to it are additive: new fields and endpoints can appear, so ignore fields you don't know. Each response says the API version in ClockLap-Api-Version (now 2026-10-01). If something is ever retired, its responses carry Deprecation and Sunset headers well before, with a Link to its replacement.

OpenAPI#

The full description of the API (OpenAPI 3.1) is at /docs/json on each region, for example ca-api.clocklap.com/docs/json. Import it into Postman or Insomnia, or generate a client from it.