Skip to content

Request an integration

Tell us which system you want to connect. We review every request weekly and publish a step-by-step guide or build the connection.

Zapier recipes

Use TimeTracking with Zapier today: Catch Raw Hook for signed webhooks, Code by Zapier for signature checks, and Webhooks by Zapier for API calls.

Last updated: · Markdown version

1. Summary

Status: Recipe. A native TimeTracking app in the Zapier directory is Planned.

Direction: Both. Triggers: TimeTracking webhooks. Actions: TimeTracking REST API calls.

2. Mechanism

  • Trigger: Webhooks by Zapier → Catch Raw Hook. It exposes the raw body (raw_body) and request headers, which you need to verify tt-signature.
  • Verify: Code by Zapier → Run JavaScript computes the HMAC and stops the Zap when it does not match.
  • Action: Webhooks by Zapier → Custom Request (or POST) to https://app.timetracking.tech/api/v1/... with header Authorization: Bearer tt_live_….

Catch Hook (non-raw) parses JSON for you but loses the exact bytes; the signature then cannot be verified reliably. Use Catch Raw Hook.

3. Prerequisites

  • Zapier plan with multi-step Zaps, Webhooks by Zapier and Code by Zapier.
  • An admin user (dashboard Regels & koppelingen) or an API key with scope admin to create webhooks. With an admin key the Zap itself can call POST /webhooks and POST /webhooks/{id}/test.
  • TimeTracking API key (write for actions, read is enough for GET /org/week, /people, /clients, /placements).

4. Entity mapping

Zapier itself holds no entities; you map fields between the trigger app and TimeTracking. Common mappings:

Zap Source fields TimeTracking call
New row in Google Sheets → record hours placement id, date, hours POST /claims {placementId, workDate, minutes, source:"import", sourceRef:"sheet-row-<n>"}
New hire in ATS → placement name, email, client, start date PUT /{clients,people,placements}/by-external/{source}/{ref}
export.completed → email accountant with the file data.runId, data.startsOn, data.endsOn, data.rows GET /exports/runs/{runId}/file (CSV, read key OK)
conflict.created → Slack/Teams/task data.workDate, data.candidates GET /placements/{placementId} for names

External IDs: upsert with PUT /{people|clients|placements}/by-external/{source}/{ref} (for example source recruitee), so no lookup step is needed. Placements reference personExternalRef / clientExternalRef of the same source. Upserts are full replaces. Add an Idempotency-Key header so Zapier auto-replays are safe.

5. Step-by-step setup

Trigger Zap: TimeTracking event → anything

  1. Webhooks by Zapier → Catch Raw Hook. Copy the hook URL.
  2. In TimeTracking (admin) create a webhook with that URL and the events you need. Copy the whsec_… secret.
  3. Code by Zapier → Run JavaScript, input data: raw = Raw Body, sig = Headers Tt Signature (header names appear in the trigger sample), secret = your secret (or store it in Zapier's Storage).
const crypto = require("crypto");
const parts = Object.fromEntries(String(inputData.sig || "").split(",").map((p) => p.trim().split("=", 2)));
const expected = crypto.createHmac("sha256", inputData.secret).update(`${parts.t}.${inputData.raw}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
if (!fresh || !parts.v1 || expected !== parts.v1) throw new Error("Invalid TimeTracking signature");
const evt = JSON.parse(inputData.raw);
return { type: evt.type, id: evt.id, ...Object.fromEntries(Object.entries(evt.data).map(([k, v]) => [k, typeof v === "object" ? JSON.stringify(v) : v])) };
  1. Filter by Zapier: continue only if type equals the event you want.
  2. Add your action steps.

Action Zap: anything → TimeTracking

Webhooks by Zapier → Custom Request:

  • Method: POST
  • URL: https://app.timetracking.tech/api/v1/claims
  • Data: {"placementId":"{{placement_id}}","workDate":"{{date}}","minutes":{{minutes}},"source":"import","sourceRef":"zap-{{row_id}}"}
  • Headers: Authorization = Bearer tt_live_…, Content-Type = application/json, Idempotency-Key = zap-{{zap_meta_id}}-{{row_id}} (8–200 chars; replays return the stored response with header idempotent-replayed: true)

Use Formatter → Numbers to convert hours to minutes (hours * 60, rounded). Dates must be YYYY-MM-DD.

6. Sync schedule and triggering events

Trigger Zaps run when TimeTracking delivers (every 5 minutes; see Webhook events). Polling is also practical now: Schedule by Zapier + GET /days?from&to&updated_since=<last run> or GET /people?updated_since=…&limit=100 (follow next_cursor).

7. Corrections, conflicts and locked periods

  • A second POST /claims for the same placement/day from the same source replaces the earlier one (newest claim per source counts). From a different source that disagrees, it creates a conflict.
  • Posting to an exported/locked day returns 423 locked; route it to a human.
  • Zapier auto-replay can resend an action: send an Idempotency-Key header, and for claims also a client-generated id (UUID) in the body.

8. Testing checklist

  • Trigger Zap: tampering with the secret makes the code step fail.
  • Action Zap: one row produces one claim; replaying the Zap run with the same id does not create a second claim.
  • Errors (422, 423) are visible in Zap history and alert someone.

9. Troubleshooting

Problem Fix
Signature never matches You used Catch Hook instead of Catch Raw Hook, or mapped a parsed field instead of Raw Body.
401 Header must be Authorization: Bearer tt_live_….
403 "API key is read-only" Use a write key for POSTs.
422 "Provide either minutes or start and end time" Send minutes (integer) or startMin + endMin.
429 Over 600 req/min per key: add Delay steps.

10. Limits and known gaps

  • No native app, no OAuth: keys are pasted into Zap steps. A native Zapier app is Planned.
  • Webhook payloads have IDs only; enrich with GET /{resource}/{id}.
  • Webhook management (create, test, replay, rotate secret) needs an admin user or an admin-scope key; prefer a separate admin key used only for setup.
  • Rotating the secret (POST /webhooks/{id}/rotate-secret) invalidates the old one immediately; update the Code step at the same time.

11. Build it with Claude

Design a Zapier Zap that receives TimeTracking webhooks via Catch Raw Hook, verifies tt-signature in a Code by
Zapier JavaScript step (v1 = hex HMAC-SHA256(secret, `${t}.${rawBody}`), 300 s tolerance), and for
conflict.created looks up names with GET https://app.timetracking.tech/api/v1/placements (Bearer key) and posts
a message to Slack. Give me every step with exact field mappings and the code, plus a second Zap that turns new
Google Sheets rows (placement_id, date, hours) into POST /claims with source "import" and a deterministic UUID id.