Factuarea API

Time clock

Clock in and out, pauses, retroactive entries and the correction workflow over the append-only time-record ledger.

Clocking writes to an append-only ledger: clocking in, pausing, resuming and clocking out each append a new entry that is never edited or deleted. Every entry is chained to the previous one with a SHA-256 hash (per-company chain), so any tampering is detectable. The live session statenot_started, working, paused, finished— is derived from the ledger, not stored in a column.

All endpoints live under https://api.factuarea.com/v1 and use the time_entries:read / time_entries:write scopes, the same error envelope and cursor pagination as the rest of the API.

Clock in, pause, resume, clock out

Four write operations drive a working day. Each takes an optional occurred_at (defaults to now) and a source, and returns the appended entry.

OperationEndpointFrom state
Clock inPOST /v1/time-entries/clock-innot_started or finished
Start a pausePOST /v1/time-entries/pauseworking
ResumePOST /v1/time-entries/resumepaused
Clock outPOST /v1/time-entries/clock-outworking or paused

Two rules govern the sequence. One open day at a time: clocking in twice returns 422 ("Ya has fichado la entrada."); pausing or clocking out with no open day returns 422. Monotonic chronology: an occurred_at earlier than the last event of the shift is rejected with 422. A day can have several shifts (jornada partida) — clocking in again after clocking out opens a brand-new shift.

curl -X POST https://api.factuarea.com/v1/time-entries/clock-in \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "source": "web" }'

Read the current open session with GET /v1/time-entries/current, list entries with GET /v1/time-entries and fetch one with GET /v1/time-entries/{time_entry} — all under time_entries:read. See the schemas in the API Reference.

Retroactive (manual) entries

POST /v1/time-entries/manual records a complete past shift (clock-in, optional pauses and clock-out) for an employee who forgot to clock. A reason is required — it is sealed into the hash chain as part of the evidence — and the entry is flagged is_retroactive with source: manual. Unlike live self-service clocking, a manual entry is a privileged action and is written to the audit log.

curl -X POST https://api.factuarea.com/v1/time-entries/manual \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "employee_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b",
    "started_at": "2026-02-03T09:00:00+01:00",
    "ended_at": "2026-02-03T17:00:00+01:00",
    "reason": "Forgot to clock in; confirmed by manager"
  }'

The correction workflow

A time record is never edited. To fix a mistake, an employee opens a correction request; a manager or admin then approves or rejects it. A request moves pending → approved or pending → rejected, both terminal.

OperationEndpointEffect
Request a correctionPOST /v1/time-correctionsCreates a pending request.
ApprovePOST /v1/time-corrections/{correction}/approveAppends a correction entry to the original; emits time_entry.corrected.
RejectPOST /v1/time-corrections/{correction}/rejectRecords a rejection with a reason; the original is untouched.
List / showGET /v1/time-corrections, GET /v1/time-corrections/{correction}Read the workflow state.

Approval appends a new entry that references the original one — the mistake and its fix both stay in the ledger. Two guards apply: you cannot approve your own request (422), and a request already resolved cannot be resolved again (422).

curl -X POST https://api.factuarea.com/v1/time-corrections/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b/approve \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "note": "Verified against the access log" }'

There is no update or delete for a time record. Every correction is a new entry that keeps the original intact — that is what makes the register defensible before the Labour Inspectorate.

Verify chain integrity

GET /v1/time-entries/chain/validate recomputes the whole hash chain and reports whether it is intact, returning the id of the first broken entry if any. It is a read-only integrity check (rate-limited) — use it to prove the register has not been altered.

Next steps

On this page