← All articles
Integrations

Connect LogTicks to your own tools: API keys and webhooks

Pull a week of hours into a spreadsheet with a personal API key, and have LogTicks tell your own system when a timesheet is approved or an invoice is paid. With curl examples and a signature check you can copy.

· 6 min read · The LogTicks team

Every firm has one system that the timesheet tool does not talk to. A payroll export somebody builds by hand each month. A finance dashboard that wants to know when an invoice is paid. A chat channel that should hear when a week is approved. A catalogue of ready-made connectors never has yours.

LogTicks does not have a catalogue. It has the two things you need to connect it yourself: API keys, for a script that asks LogTicks a question or tells it something, and webhooks, for LogTicks to tell your system that something happened. This article uses both.

API keyWebhook
DirectionYour script calls LogTicksLogTicks calls your endpoint
Good forExports, imports, scheduled reportsReacting when something changes
Who sets it upAny user, for themselvesA manager, for the company
WhereUser Settings, API KeysCompany Settings, Webhooks

Part 1: API keys

Create a key

  1. Open User Settings, API Keys and press Create key.
  2. Give it a Name that says what it is for, such as "Payroll export script".
  3. Choose Access: Read only or Read and write.
  4. Choose when it Expires. The default is 90 days.
  5. Press Create key and copy it. It is shown once; only a hash is stored.
The API keys page: one key named Weekly hours export with its prefix, read-only access, active status, created and expiry dates and last used, a curl example, notes on what a key can do, and a company switch to allow API keys
The key list shows a prefix, never the key. "Last used" tells you which keys are safe to revoke.

A key looks like lt_ followed by letters and numbers. Send it as a bearer token:

export LOGTICKS_API=https://api.logticks.com/api
export LOGTICKS_KEY=lt_xxxxxxxxxxxx_your_key_here

curl -s "$LOGTICKS_API/user/user" \
  -H "Authorization: Bearer $LOGTICKS_KEY"
Who am I? A good first call: it returns your name, role and company.

What a key can and cannot do

  • It acts as you. It has your role and your company. A member's key cannot approve timesheets; a manager's can. If your role changes, so does the key's reach.
  • Read only means GET only. Anything else is refused.
  • Some things are closed to every key: signing in, two-factor, passwords and profile, user administration, API keys themselves, webhooks, reminder settings and the audit log.
  • It can be stopped. You can revoke it; a manager can revoke it; a company can switch API keys off for everyone. Deactivating your account revokes all your keys.

Read a week of hours

curl -s "$LOGTICKS_API/timesheet/weekly?week_start=2026-11-09" \
  -H "Authorization: Bearer $LOGTICKS_KEY"
week_start is the Monday of the week.
{"week_start": "2026-11-09T00:00:00", "week_end": "2026-11-15T00:00:00",
 "status": "draft", "total_hours": 41.0,
 "entries": [{"date": "2026-11-13", "project_id": 1, "hours": 1.0,
              "description": "Team retro"}]}
The answer (shortened).

Log time

With a read-and-write key, set your hours on one project for one day. project_id is the numeric id from the project list.

curl -s -X POST "$LOGTICKS_API/timesheet/day-entry" \
  -H "Authorization: Bearer $LOGTICKS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"date": "2026-11-14", "project_id": 4, "hours": 1.5,
       "description": "Data migration dry run"}'

Time logged through the API goes through the same checks as time typed into the grid. A project rule that blocks the entry answers 400 with the rule's message, and rules that only warn come back in a warnings list.

Be a good client

Each key has its own allowance of requests per minute, and exports and bulk writes have tighter ones. When you exceed a limit the API answers 429 with a Retry-After header in seconds. Wait that long and try again. Some older endpoints report a failure as HTTP 200 with "status": "FAIL", so check the status field as well as the HTTP code.

Part 2: webhooks

Register an endpoint

  1. A manager opens Company Settings, Webhooks and presses Add webhook.
  2. Enter the Endpoint URL. It must be an https:// address reachable from the internet.
  3. Add a Description of what receives the events.
  4. Tick the events you want.
  5. Press Add webhook and copy the signing secret. It starts with whsec_ and is shown once.
The Add webhook dialog with an endpoint URL, a description Finance dashboard, and a list of events grouped under timesheets, leave, invoices and projects with timesheet.approved, invoice.sent and invoice.paid ticked
Choosing events. Take only the ones you will act on.
GroupEvents
Timesheetstimesheet.submitted, .approved, .rejected, .changes_requested, .client_approved, .client_changes_requested
Leaveleave.requested, leave.approved, leave.rejected
Invoicesinvoice.created, invoice.sent, invoice.paid, invoice.void
Projects and peopleproject.created, user.joined
Daily statusdaily_status.posted

What arrives

POST /your/endpoint
X-LogTicks-Event: timesheet.submitted
X-LogTicks-Delivery: 22324c1b-1ca3-4b54-bfd5-bf25d96f06d2
X-LogTicks-Attempt: 1
X-LogTicks-Signature: t=1794678710,v1=5f2c...

{"id": "evt_8f465d8b6a414cb097359c671669e91e",
 "type": "timesheet.submitted",
 "created": "2026-11-14T17:51:50.328854+00:00",
 "company_guid": "77b6f0ed-...",
 "data": {"timesheet_guid": "4ff16566-...", "employee_name": "Liam Chen",
          "week_start": "2026-11-09", "week_end": "2026-11-15",
          "status": "submitted", "total_hours": 42.5}}
data carries identifiers and a few fields. Fetch the record with an API key if you need more.

Check the signature

Anyone who learns your endpoint's address can post to it. The signature is how you know a delivery came from LogTicks. The header has a timestamp t and a value v1, which is the hex HMAC-SHA256, keyed with your signing secret, of the timestamp, a dot, and the raw request body.

import hashlib, hmac, time

def verify(secret: str, header: str, raw_body: bytes, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > tolerance:
        return False          # too old: refuse replays
    signed = parts["t"].encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
Python. Use the raw bytes of the body, before any JSON parsing.

Three mistakes account for most failed checks: verifying a re-serialised body instead of the raw bytes, comparing with == instead of a constant-time comparison, and skipping the timestamp check.

Delivery rules

RuleDetail
Answer quicklyAny 2xx within 5 seconds. Do the work after you have answered
RetriesAfter 1 minute, 5 minutes, 30 minutes and 2 hours, then the delivery is marked failed
DuplicatesPossible. The event id stays the same across retries, so ignore one you have handled
OrderNot guaranteed
Repeated failureAfter many failed attempts in a row the webhook is switched off and flagged until a manager switches it back on
RedirectsNot followed
A webhook's Deliveries panel with Send test event and Refresh buttons and one delivery of a test event marked Failed, with the time, duration, attempt number and the reason: the host name does not resolve
The delivery log. This test event failed because the demo endpoint does not exist, and the log says why. A failed delivery can be redelivered from here.

Use Send test event while you build the receiver. It sends a webhook.test event through the same signing and delivery path.

  • One key per script, named for what it does
  • Read only unless the script must write
  • An expiry on every key, and a reminder to renew it
  • Keys kept out of source control
  • Webhook receivers verify the signature and de-duplicate on the event id

What it doesn't do (yet)

  • No ready-made connectors. There is no Slack, Microsoft Teams or Zapier integration. You write the receiver.
  • Keys are personal. There is no company-wide service account; a key acts as the person who made it and stops when they are deactivated.
  • Two response styles. Older endpoints wrap results in an envelope with a status field; newer ones return the result directly and use HTTP status codes for errors. Check both.
  • Webhooks need a public https address. Private and local addresses are refused, so you cannot point one at a laptop without a tunnel.
  • At-least-once, unordered. Build the receiver to tolerate a duplicate and an out-of-order event.

A key to ask, a webhook to be told. Everything else is your code.

Create a key

Make a read-only key, run the first curl command, and read your own week.

Start free