Time logged on tasks
Log time on tasks by hand or with a timer, read the time summary of a project and turn billable hours into a draft invoice — and why it is not the working-day clock-in.
Time logged on a task measures the work spent on it, so you can see where the hours go and bill them. You log it by hand, as a closed period, or with a timer that runs until you stop it, and a project turns its billable hours into a draft invoice.
This is not the working-day register. A task time entry belongs to the
tasks module: it uses the tasks:* scopes, its events are
task_time_entry.* and its operations live under /v1/tasks/{task}/time-entries
and /v1/task-timers. The clock-ins of the Time tracking module
(/v1/time-entries, time_entries:*, the control_horario module) are the
legal, hash-chained working-day record — a different resource with its own
register. Hours logged on a task never appear in that register, in its
balances, in monthly closes or in payroll exports. See
Time tracking.
Log time by hand
POST /v1/tasks/{task}/time-entries
logs a closed period of work:
curl -X POST https://api.factuarea.com/v1/tasks/$TASK_ID/time-entries \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"started_at": "2026-09-28T09:00:00+02:00",
"ended_at": "2026-09-28T11:00:00+02:00",
"description": "Payment gateway integration",
"billable": true
}'started_atandended_atare ISO 8601 with a time zone. The period must end after it starts, last at most 24 hours and not start more than a day in the future; otherwise the call returns 422invalid_time_entry_period. An entry with equal start and end is valid and lasts zero seconds.descriptionis optional, up to 1,000 characters.billabledefaults totrue.- The author is always the holder of the credential.
- Entries may overlap: this is work you attribute to tasks, not a clock-in.
The entry comes back with its duration_seconds, is_running, and, once it is
billed, the invoice_id and the invoiced_at of the invoice that includes it.
The timer
Instead of typing a period, start a timer when you begin and stop it when you finish:
# Start it on a task (no body)
curl -X POST https://api.factuarea.com/v1/tasks/$TASK_ID/timer/start \
-H "Authorization: Bearer $FACTUAREA_API_KEY"
# What is running right now? `data` is null when nothing is
curl https://api.factuarea.com/v1/task-timers/current \
-H "Authorization: Bearer $FACTUAREA_API_KEY"
# Stop it, whichever task it runs on
curl -X POST https://api.factuarea.com/v1/task-timers/stop \
-H "Authorization: Bearer $FACTUAREA_API_KEY"- One timer per user and company. Starting another while one runs, on any
task, returns 409
task_timer_already_runningand leaves the running one untouched. A user with a timer in another company can start one here. - A running timer is an entry with
ended_at: nullandis_running: true. - Stopping fixes the end at the current instant and returns the closed entry.
With no timer running it returns 422
task_timer_not_running. - A timer left running for more than 24 hours is closed at exactly 24 hours after its start, so a forgotten timer never blocks the next one.
- The timer belongs to the holder of the credential, so
GET /v1/task-timers/currentshows their timer and nobody else's.
Edit and delete entries
PUT /v1/tasks/{task}/time-entries/{time_entry} is partial and changes
started_at, ended_at, description and billable. On a running entry you
can only change the description and the billable flag: stop the timer to close it.
Once an entry is included in an invoice it is locked: editing or deleting it
returns 422 task_time_entry_invoiced until
the draft invoice is deleted. DELETE .../time-entries/{time_entry} is permanent
and needs the tasks:delete scope and an Idempotency-Key; deleting a running
entry cancels the timer.
GET /v1/tasks/{task}/time-entries
lists the entries of a task, newest first and cursor-paginated.
Time summary of a project
GET /v1/projects/{project}/time-summary
answers how much time a project has consumed, optionally between from and to
(dates in the company time zone):
{
"data": {
"object": "project_time_summary",
"project_id": "0193a4f2-7c20-7a11-8b52-4d6e8f0a2c01",
"from": "2026-09-01",
"to": "2026-09-30",
"total_seconds": 36900,
"billable_seconds": 33300,
"invoiced_seconds": 9900,
"pending_seconds": 23400,
"by_task": [
{
"task_id": "0193a4f2-8f53-7d44-8e85-7a9b1c3d5f12",
"key": "DEV-12",
"title": "Integrate the payment gateway in the checkout",
"total_seconds": 22500,
"billable_seconds": 22500,
"invoiced_seconds": 9900,
"pending_seconds": 12600
}
],
"by_user": [
{
"user_id": "0193a4f2-6b1c-7d3e-8a41-2c5e7f9a1b01",
"name": "Ana Pérez",
"total_seconds": 22500,
"billable_seconds": 22500
}
]
}
}pending_seconds is what you can still bill: the billable time not yet invoiced.
Running timers are left out of every total.
Invoice the hours
A project turns its billable hours into a draft invoice for its billing
contact. Two things must be set on the project, with
PUT /v1/projects/{project} or in the app:
- a billing contact (
billing_contact_id) that can receive sales invoices; - a price: an hourly rate (
billing_hourly_rate) or a billing product (billing_product_id), whose price for that contact is used when there is no rate.
Without them, the call returns 422
task_time_not_invoiceable and says what is
missing.
Choose the hours with either entry_ids (up to 500 entries) or a
period (from and to, inclusive, by the start date of the entries) — never
both — and a grouping: per_task gives one line per task, per_entry one line
per entry and single_line a single line with the total. Only closed, billable,
not yet invoiced entries of the project qualify.
Preview first. It applies the same checks and creates nothing:
curl -X POST https://api.factuarea.com/v1/projects/$PROJECT_ID/time-invoices/preview \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "period": { "from": "2026-09-01", "to": "2026-09-30" }, "grouping": "per_task" }'The preview lists the customer, the lines (quantity in hours, unit HUR, price
and amount before taxes), the totals in seconds and hours, the entries that would
be invoiced and any warnings. Then create the invoice with the same body:
curl -X POST https://api.factuarea.com/v1/projects/$PROJECT_ID/time-invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "period": { "from": "2026-09-01", "to": "2026-09-30" }, "grouping": "per_task" }'{
"data": {
"id": "0193a4f2-c3fd-7bee-8c8f-7e9f1a3b5d01",
"object": "task_time_invoice",
"project_id": "0193a4f2-7c20-7a11-8b52-4d6e8f0a2c01",
"invoice_id": "0193a4f2-c3fd-7bee-8c8f-7e9f1a3b5d10",
"grouping": "per_task",
"status": "draft"
}
}- It needs
invoices:write, not a project scope: the operation creates an invoice. A credential with onlyprojects:writegets 403insufficient_scope. The preview is a read and needsprojects:read. - The invoice is a draft. It is never issued, sent or filed with the AEAT on
its own; you finish it with the invoices API. The normal
invoice rules apply: if the plan has used up its invoices, the call returns 402
limit_exceededand nothing is created. - Everything or nothing. In one transaction the invoice is created and the entries are marked as invoiced. If any step fails, no entry is marked.
- Retries are safe. Repeating the request with the same
Idempotency-Key, or with the samebatch_id, returns the same invoice. Two requests at once over the same entries produce one invoice; the other getstask_time_entry_invoiced. - Deleting the draft releases the hours. They become pending again. Issuing, sending, collecting or annulling the invoice does not release them.
Events
Time entries emit task_time_entry.created (also when a timer starts),
task_time_entry.updated (also when a timer stops), task_time_entry.deleted
and task_time_entry.invoiced, one per entry of an invoiced batch. See
Task events.
Next steps
projects, board, tasks, labels, comments and attachments.
finish, issue and send the draft that the hours produce.
the legal working-day register, which is a different thing.
create_task_time_entry, start_task_timer and invoice_project_time for an agent.
factuarea tasks time-entries and factuarea task-timers.