Hourtick Team API
Approvals, invoicing and members (role-gated).
Approvals, invoicing and members (role-gated).
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/hourtick:hourtick-team-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Hourtick Team API
version: 1.0.0
description: 'The Hourtick REST API. Authenticate with a personal API token: create one in **Settings → API tokens**, then send `Authorization: Bearer ht_…`.
All timer changes go through **POST /api/v1/commands**, the same endpoint the web and Mac apps use. Commands are idempotent per `commandId` (use a new UUID per action, reuse it to retry safely).
Every response carries a `Server-Time` header (epoch ms). Errors use RFC 9457 problem details.'
servers:
- url: https://hourtick.com
security:
- bearer: []
tags:
- name: Team
description: Approvals, invoicing and members (role-gated).
paths:
/api/v1/time-entries:
patch:
tags:
- Team
summary: Edit one or many entries (managers and up)
description: 'Applies one `patch` to every listed entry: notes, project and task type (billable follows them), date, duration, or `userId` to move time to another person. Pass each entry''s current `version`. All or nothing: running, invoiced, submitted or approved entries, stale versions and days in the new person''s submitted weeks return 409 with `conflicts`. Up to 500 entries per request.'
requestBody:
required: true
content:
application/json:
schema:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
entries:
minItems: 1
maxItems: 500
type: array
items:
type: object
properties:
id:
type: string
format: uuid
pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
version:
type: integer
minimum: 1
maximum: 9007199254740991
required:
- id
- version
patch:
type: object
properties:
projectId:
type: string
format: uuid
pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
taskId:
type: string
format: uuid
pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
notes:
type: string
maxLength: 5000
spentDate:
type: string
durationSeconds:
type: integer
minimum: 0
maximum: 86400
userId:
type: string
minLength: 1
maxLength: 100
additionalProperties: false
required:
- entries
- patch
example:
entries:
- id: 0192f0c4-7d1e-7abc-9def-0123456789ab
version: 3
patch:
notes: Sprint review
durationSeconds: 5400
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
updated:
type: integer
entries:
type: array
items:
$ref: '#/components/schemas/TimeEntry'
'401':
description: Missing or invalid token
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'403':
description: Members can't edit other people's time
'409':
description: Nothing changed; see `conflicts`
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'422':
description: Invalid request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
operationId: patchApiV1TimeEntries
x-operation-id-source: derived
/api/v1/timesheets:
get:
tags:
- Team
summary: Your status for a week
parameters:
- name: weekStart
in: query
required: true
schema:
type: string
format: date
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
operationId: getApiV1Timesheets
x-operation-id-source: derived
post:
tags:
- Team
summary: Submit or withdraw your week
requestBody:
required: true
content:
application/json:
schema:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
weekStart:
type: string
action:
type: string
enum:
- submit
- withdraw
required:
- weekStart
- action
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
'409':
description: Wrong state
operationId: postApiV1Timesheets
x-operation-id-source: derived
/api/v1/approvals:
get:
tags:
- Team
summary: Weeks waiting for approval (managers)
parameters:
- name: status
in: query
schema:
type: string
enum:
- submitted
- approved
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
'403':
description: Managers only
operationId: getApiV1Approvals
x-operation-id-source: derived
post:
tags:
- Team
summary: Approve, send back or reopen a week (managers)
requestBody:
required: true
content:
application/json:
schema:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
userId:
type: string
minLength: 1
maxLength: 100
weekStart:
type: string
action:
type: string
enum:
- approve
- reject
- reopen
required:
- userId
- weekStart
- action
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
operationId: postApiV1Approvals
x-operation-id-source: derived
/api/v1/invoicing:
post:
tags:
- Team
summary: Mark entries as invoiced or not (admins; locks entries)
requestBody:
required: true
content:
application/json:
schema:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
entryIds:
minItems: 1
maxItems: 5000
type: array
items:
type: string
format: uuid
pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
invoiced:
type: boolean
required:
- entryIds
- invoiced
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
updated:
type: integer
operationId: postApiV1Invoicing
x-operation-id-source: derived
/api/v1/members:
get:
tags:
- Team
summary: Workspace members (managers)
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
operationId: getApiV1Members
x-operation-id-source: derived
/api/v1/cost-rates:
get:
tags:
- Team
summary: Every member's hourly cost rates, newest first (admins)
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
description: '{ rates: [{ userId, hourlyCostMinor, validFrom }] }'
'403':
description: Admins only
operationId: getApiV1CostRates
x-operation-id-source: derived
/api/v1/members/{userId}/cost-rate:
put:
tags:
- Team
summary: Set what an hour of a member costs, from a day on (admins).
parameters:
- name: userId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
hourlyCost:
anyOf:
- type: number
minimum: 0
maximum: 1000000
- type: 'null'
description: What an hour of this person costs, in the workspace currency; null removes the rate that starts on validFrom
validFrom:
description: Applies to time spent on or after this day; leave out to apply to all time
type: string
required:
- hourlyCost
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
'403':
description: Admins only
'404':
description: Not a member
operationId: putApiV1MembersByUserIdCostRate
x-operation-id-source: derived
components:
schemas:
Problem:
type: object
properties:
type:
type: string
title:
type: string
status:
type: integer
detail:
type: string
entry:
$ref: '#/components/schemas/TimeEntry'
conflicts:
type: array
description: 'Bulk edits: entries that blocked the change.'
items:
type: object
properties:
id:
type: string
reason:
type: string
TimeEntry:
type: object
properties:
id:
type: string
format: uuid
workspaceId:
type: string
format: uuid
userId:
type: string
projectId:
type: string
format: uuid
taskId:
type: string
format: uuid
description: Task type
workItemId:
type:
- string
- 'null'
format: uuid
description: The task this time is for, if any
notes:
type: string
spentDate:
type: string
format: date
description: The day the time belongs to (user's time zone).
timezone:
type: string
examples:
- Europe/Copenhagen
accumulatedSeconds:
type: integer
description: Seconds banked from completed runs.
timerStartedAt:
type:
- integer
- 'null'
description: Epoch ms when the current run started; null when stopped.
billable:
type: boolean
lockedReason:
type:
- string
- 'null'
enum:
- null
- invoiced
- approved
- submitted
version:
type: integer
deletedAt:
type:
- integer
- 'null'
createdAt:
type: integer
updatedAt:
type: integer
description: Live duration = accumulatedSeconds + (now − timerStartedAt)/1000 while running.
securitySchemes:
bearer:
type: http
scheme: bearer
bearerFormat: ht_… personal API token