REST hooks for Zapier, Make and n8n
Let Zapier, Make and n8n subscribe to TimeTracking events themselves: POST /hooks to subscribe, DELETE /hooks/{id} to unsubscribe, GET /hooks/samples for field mapping, with setup steps per tool.
Last updated: · Markdown version
1. Summary
Status: Live (REST hooks API). Listed marketplace apps for Zapier and Make and a community node for n8n are still Planned; until then you build the trigger in each tool's developer platform or with plain HTTP steps, as described below.
Direction: TimeTracking to the tool (triggers). For actions (the tool writing to TimeTracking) use the REST API as in the Zapier, Make and n8n recipes.
A REST hook is a webhook that the automation tool creates and removes itself: when a zap, scenario or workflow is switched on, the tool calls POST /hooks with its own receiving URL; when it is switched off, it calls DELETE /hooks/{id}. No one has to copy URLs or secrets between the dashboard and the tool.
2. Endpoints
All endpoints need an API key with the admin scope (dashboard Rules & integrations → API keys → Admin), because a hook sends organisation data to a URL. Use a separate key per tool.
| Call | Purpose |
|---|---|
POST /api/v1/hooks |
Subscribe. Body: url (aliases target_url, hookUrl), event or events, optional source (zapier, make, n8n, …) and name. Returns the subscription with id and the signing secret (once). |
DELETE /api/v1/hooks/{id} |
Unsubscribe (deletes the webhook). Answers { "deleted": true }; 404 when it is already gone. |
GET /api/v1/hooks/{id} |
Does the subscription still exist? (n8n checkExists.) |
GET /api/v1/hooks |
Subscriptions made through POST /hooks. |
GET /api/v1/hooks/samples?event=… |
Example events (the exact delivered envelope) for field mapping. Without event: one example per event type. Any valid key. |
curl -s -X POST "$TT/hooks" -H "Authorization: Bearer $ADMIN_KEY" -H "content-type: application/json" \
-d '{"target_url":"https://hooks.zapier.com/hooks/standard/123/abc/","event":"timesheet.submitted","source":"zapier"}'
{ "data": { "id": "6b0f…", "url": "https://hooks.zapier.com/hooks/standard/123/abc/", "events": ["timesheet.submitted"],
"source": "zapier", "name": "Zapier", "active": true, "created_at": "2026-10-30T09:12:01.120Z", "secret": "whsec_…" } }
Deliveries are ordinary webhooks: an HTTPS POST with the envelope { id, type, created, data }, the tt-signature header, retries with backoff (7 attempts over about 21 hours), a delivery log and replay. The URL must be https:// and resolve to a public address. An organisation can have at most 100 REST hooks. Subscriptions show on the dashboard (Rules & integrations → Zapier, Make and n8n), where an admin can delete them.
The events are listed in the event catalogue; GET /hooks/samples returns the same shapes.
3. Zapier
In the Zapier Platform UI (or CLI) create a private integration:
- Authentication → API Key: one field
api_key; add the headerAuthorization: Bearer {{bundle.authData.api_key}}to every request. Test call:GET https://app.timetracking.tech/api/v1/hooks/samples?event=webhook.test. - Trigger → REST Hook, one trigger per event (for example "Hours submitted" =
timesheet.submitted):- Subscribe:
POST https://app.timetracking.tech/api/v1/hooks, body{"target_url": "{{bundle.targetUrl}}", "event": "timesheet.submitted", "source": "zapier"}. - Unsubscribe:
DELETE https://app.timetracking.tech/api/v1/hooks/{{bundle.subscribeData.data.id}}. - Perform List (sample data and polling fallback):
GET https://app.timetracking.tech/api/v1/hooks/samples?event=timesheet.submitted, and returnresponse.data(an array withid). - Perform: return
[bundle.cleanedRequest](the delivered event).
- Subscribe:
- Actions: Create claim, Upsert person by external ID and so on, as plain API requests (see the Zapier recipe). Send an
Idempotency-Keyheader.
Without your own integration, the Catch Raw Hook recipe still works: create the webhook yourself in the dashboard.
4. Make
In Make's custom app editor (My apps → Create a new app):
- Connection: one parameter
apiKey; base headersAuthorization: Bearer {{connection.apiKey}}. - Webhook (type dedicated, attached):
- Attach:
POST https://app.timetracking.tech/api/v1/hooks, body{"url": "{{webhook.url}}", "events": {{parameters.events}}, "source": "make"}, response"data": {"externalHookId": "{{body.data.id}}"}. - Detach:
DELETE https://app.timetracking.tech/api/v1/hooks/{{webhook.externalHookId}}.
- Attach:
- Instant trigger using that webhook; Samples: paste a result of
GET /hooks/samples?event=…, or call it in the module's epoch/sample request.
Without a custom app: create a Custom webhook in a scenario, then register its URL once with POST /hooks (HTTP module or curl) and remove it with DELETE /hooks/{id} when the scenario is retired.
5. n8n
For a community/custom node, implement the trigger node's webhookMethods.default:
checkExists:GET /api/v1/hooks/{{webhookData.hookId}}→ true on 200, false on 404.create:POST /api/v1/hookswith{"url": this.getNodeWebhookUrl("default"), "events": [...], "source": "n8n"}; storedata.idaswebhookData.hookIdanddata.secretto verifytt-signatureinwebhook().delete:DELETE /api/v1/hooks/{{webhookData.hookId}}.
Without a custom node: add a Webhook trigger node (POST, response 200), activate the workflow, and register the production URL once with an HTTP Request node (POST /hooks, header Authorization: Bearer <admin key>). Use GET /hooks/samples?event=… to pin test data. Verify the signature with a Crypto node as in the n8n recipe.
6. Limits and errors
| Answer | Meaning |
|---|---|
403 forbidden "Admin role required" |
The key has no admin scope. |
422 invalid |
No url/event, an unknown event, an http:// URL, or a URL that resolves to a private address. |
409 conflict reason: hook_limit |
100 REST hooks in the organisation; delete unused ones. |
404 not_found on DELETE |
Already removed (treat as success). |
429 rate_limited |
Over the key's budget; see rate limits and the X-RateLimit-* headers. |
Rotate a hook's secret with POST /webhooks/{id}/rotate-secret; replay a failed delivery with POST /webhook-deliveries/{id}/replay.