Skip to content

Start a timer

POST
/api/time-entries/timer/start
curl --request POST \
--url https://invoicing-api.eddish.app/api/time-entries/timer/start \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "workTaskId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "notes": "example", "spentDate": "2026-04-15", "hours": 1, "startedTime": "example", "localTime": "example" }'

Starts a timer on a new time entry; every start is its own entry. Only one timer can run at a time. With a start time, the start is snapped down to the quarter hour and moved forward past your other entries that day if it would overlap them. Without one, the timer has no times: it counts from now, and stopping it records the exact hours.

Media typeapplication/json

The details of a new timer. Every timer start is a new time entry.

object
projectId
required
string format: uuid
workTaskId
required
string format: uuid
notes
required
null | string
spentDate
required

The caller’s local date today, as yyyy-MM-dd. The entry is dated with it.

string format: date
hours

No longer used: a timer starts at zero and runs from its start time. Still accepted so older clients don’t fail.

null | number | string format: double
/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/
startedTime

The time of day the timer started, as HH:mm:ss. It is snapped down to the quarter hour, and moved forward past your other entries that day if it would overlap them. Leave empty for a timer without times, counted from now and stopped to the exact hours.

null | string format: time
localTime

The caller’s local time of day right now, as HH:mm:ss, so a start time in the past can be counted from. Leave empty to use the account’s time zone.

null | string format: time

Example generated

{
"projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"workTaskId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"notes": "example",
"spentDate": "2026-04-15",
"hours": 1,
"startedTime": "example",
"localTime": "example"
}

OK

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data
One of:
null
error
One of:
null

Example generated

{
"success": true,
"data": {
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"projectName": "example",
"clientId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"clientName": "example",
"workTaskId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"workTaskName": "example",
"userId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"userName": "example",
"spentDate": "2026-04-15",
"hours": 1,
"notes": "example",
"startedTime": "example",
"endedTime": "example",
"timerStartedAtUtc": "2026-04-15T12:00:00Z",
"isRunning": true,
"isLocked": true,
"billable": true,
"billableRate": 1,
"isBilled": true,
"invoiceId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"createdAtUtc": "2026-04-15T12:00:00Z"
},
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

Not signed in, or the credential isn’t recognised, has expired or was revoked: auth.missing_credential, auth.invalid_credential, auth.expired_credential, auth.revoked_credential.

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

Not allowed. error.code says why: auth.role_not_permitted (your role doesn’t allow it), auth.read_only_token (the token can only read), app.no_seat (no seat in this App), auth.api_access_not_in_plan (the plan doesn’t include API access), auth.not_a_member (no longer a member of the Account), auth.insufficient_scope (an Account API key without the scope).

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

The Account is over its plan’s limits, so changes are refused until an Owner or Admin sorts that out: plan.read_only.

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

Too many requests: rate_limit.exceeded. Wait the number of seconds in the Retry-After header.

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}
Retry-After
integer

Seconds to wait before trying again.