Hourtick API V1 API

The Hourtick REST API. Authenticate with a personal API token: create one in **Settings → API tokens**, then send `Authorization: Bearer ht_…`.

Operations 134

GET /api/v1/bootstrap Current user, workspace, projects and task types
GET /api/v1/time-entries Your time entries in a date range, plus the running entry
PATCH /api/v1/time-entries Edit one or many entries (managers and up)
POST /api/v1/commands Start, stop, continue, create, edit, delete or restore time
GET /api/v1/reports Totals, groups and entries for a period (team scope for managers). For admins, totals and groups also carry the P&L: `peopleCostMinor` (hours × each person's cost rate on that day)
GET /api/v1/stream Server-Sent Events: live entry changes for your account and task changes in the workspace
GET /api/v1/timesheets Your status for a week
POST /api/v1/timesheets Submit or withdraw your week
GET /api/v1/approvals Weeks waiting for approval (managers)
POST /api/v1/approvals Approve, send back or reopen a week (managers)
POST /api/v1/invoicing Mark entries as invoiced or not (admins; locks entries)
GET /api/v1/members Workspace members (managers)
GET /api/v1/cost-rates Every member's hourly cost rates, newest first (admins)
PUT /api/v1/members/{userId}/cost-rate Set what an hour of a member costs, from a day on (admins). Effective-dated: time is costed at the rate valid on its day
GET /api/v1/agent-costs Agent cost ledger for a period: an agent's own rows, or all for admins (`agentId` narrows it)
POST /api/v1/agent-costs Report what a piece of work cost (agent token: its own; admins: give agentId). Counts toward the agent's budget and the P&L. Report each cost once (here, MCP log_time usage, or ses
GET /api/v1/ai-providers AI provider connections for verified agent cost (admins). Keys are never returned, only their last 4 characters
POST /api/v1/ai-providers Connect Anthropic (Admin API key), OpenAI (admin key) or OpenRouter (management key) to read what they billed (admins). The key is checked against the provider, then stored encrypt
PATCH /api/v1/ai-providers/{id} Rename, or turn filling in from bills on or off (admins)
DELETE /api/v1/ai-providers/{id} Remove the key, its synced bills and the cost it filled in (admins)
GET /api/v1/ai-providers/{id}/scopes What the key can see (Anthropic workspaces, OpenAI projects and API keys, OpenRouter keys) and which agent each is mapped to (admins)
PUT /api/v1/ai-providers/{id}/scopes Map a scope to an agent, or clear it with agentId null. A scope belongs to one agent (admins)
POST /api/v1/ai-providers/{id}/sync Read the last 30 days of bills now and reconcile (admins). Runs every 6 hours on its own
GET /api/v1/reports/cost-reconciliation Agent cost as reported vs as billed by the providers, per agent and UTC month, with what was filled in (source `provider` in the cost ledger) (admins)
GET /api/v1/reports/agent-outcomes Agent outcomes: tasks with agent work done in the period, with the agent's time and cost plus people's time before (briefing) and after (review and rework) the agent delivered, how
GET /api/v1/client-ai-reports/preview Client AI work report without sharing it: people's and agents' hours, agents and models, and whether a person reviewed each piece of agent work. No rates, costs or notes (managers)
GET /api/v1/client-ai-reports Shared client AI work reports, newest first (managers). Links are not listed
POST /api/v1/client-ai-reports Freeze a client AI work report and get a share link (admins). The link (`link.url`) is only in this response; the page is /ai-report/{token} and its JSON /api/ai-reports/{token}, w
DELETE /api/v1/client-ai-reports/{id} Revoke a shared report: its link stops working at once (admins)
GET /api/ai-reports/{token} A shared client AI work report as JSON, no account needed (the link is the credential). CORS open
DELETE /api/v1/agent-costs/{id} Remove a wrong cost row (admins)
GET /api/v1/integrations Linear, Notion, Teamwork and Asana: whether each is available and whether you are connected
POST /api/v1/integrations/{provider} Start connecting: returns the vendor's approval address to open in a browser
DELETE /api/v1/integrations/{provider} Disconnect: forgets the tokens and asks the vendor to revoke them where it supports that
GET /api/v1/work-items/{id}/links Items linked to a task, with their current title and status (re-read when older than 5 minutes)
POST /api/v1/work-items/{id}/links Link a Linear issue, Notion page, Teamwork task or Asana task by its address
DELETE /api/v1/work-items/{id}/links/{linkId} Remove a link. Time already sent to Teamwork stays there
GET /api/v1/notes Notes on a project (`projectId`) or client (`clientId`): titles and previews, newest edit first
POST /api/v1/notes Create a note on a project or client
GET /api/v1/notes/{id} One note: title, Tiptap `content`, `markdown`, plain `text` and `version`
PATCH /api/v1/notes/{id} Save a note
DELETE /api/v1/notes/{id} Delete a note (its creator or a manager)
GET /api/v1/files Files on a project (`projectId`) or client (`clientId`), newest first
POST /api/v1/files Upload a file to a project or client (multipart: file, projectId or clientId; up to 25 MB; counts toward usage)
GET /api/v1/files/{id} Open a file: redirects to a short-lived signed URL (`?download=1` to download, `?format=json` for `{ url }`)
DELETE /api/v1/files/{id} Delete a file (its uploader or a manager)
GET /api/v1/records/counts Notes and files per project and client
GET /api/v1/clients Clients with VAT numbers and active project counts
POST /api/v1/clients Create a client (admins, or an agent an admin allowed), optionally with a VAT number. Records who added it
GET /api/v1/payments The workspace's Stripe connection: connected, ready to send invoices, and (admins) account, VAT and payment-term settings
PATCH /api/v1/payments VAT rate (basis points) and label, and default days until due (admins)
DELETE /api/v1/payments Stop sending invoices through the account (admins). The Stripe account, its money and sent invoices stay the business's
POST /api/v1/payments/connect Start or resume Stripe-hosted onboarding and get its one-time URL (admins, people only). The first time it needs `country`, where the business is registered
POST /api/v1/invoices/preview What an invoice would contain: lines, VAT, total, and anything that stops sending (admins; agents for their clients). Changes nothing
GET /api/v1/invoices Invoices, newest first (admins; agents see their clients')
POST /api/v1/invoices Create an invoice from a client's billable time not invoiced yet and send it through Stripe. The time is locked as invoiced. An agent outside its mandate gets status `pending_appro
GET /api/v1/invoices/{id} One invoice with its lines, payment page and timesheet link
POST /api/v1/invoices/{id}/approve Approve an agent's invoice waiting outside its mandate, and send it (admins)
POST /api/v1/invoices/{id}/retry Send an invoice again that didn't reach Stripe (admins). Nothing is created twice
POST /api/v1/invoices/{id}/void Void an invoice (or discard one not sent); its time becomes billable again (admins). A paid invoice is refunded in Stripe instead
GET /api/v1/invoice-mandates What agents may invoice without approval (admins; an agent sees its own)
POST /api/v1/invoice-mandates Let an agent send invoices on its own: clients (empty: any it works on), most per invoice (minor units, workspace currency), expiry (admins)
DELETE /api/v1/invoice-mandates/{id} Stop an invoice mandate; the agent's invoices wait for approval again (admins)
PATCH /api/v1/clients/{id} Rename a client, set or clear its VAT number, set its billing details for invoices (email, address, country, language, VAT treatment; people only), or archive it (admins)
POST /api/v1/projects Create a project (admins, or an agent an admin allowed). Records who added it
PATCH /api/v1/projects/{id} Update a project (admins): billing, budget, rate, task types, archive, and memberIds (the team; empty means everyone can track on it)
GET /api/v1/work-items Tasks on the board: open ones plus done in the last 14 days (`done=all` for everything)
POST /api/v1/work-items Create a task (any member)
GET /api/v1/work-items/{id} A task with description, checklist, timeline (comments, files, events) and time per person
PATCH /api/v1/work-items/{id} Update a task. Moving between columns: send `status` (and `position` to place it; lower is higher up)
DELETE /api/v1/work-items/{id} Delete a task with its comments and files (creator or managers). Tracked time is kept
GET /api/v1/work-items/{id}/comments The task's comments, oldest first, with their files (an agent's comment has `meta.agentId`)
POST /api/v1/work-items/{id}/comments Post in the task's chat; `attachmentIds` links files you uploaded
PATCH /api/v1/work-items/{id}/comments/{commentId} Edit your comment
DELETE /api/v1/work-items/{id}/comments/{commentId} Delete a comment and its files (author or managers)
POST /api/v1/work-items/{id}/checklist Add a checklist item
PATCH /api/v1/work-items/{id}/checklist/{itemId} Tick, rename or move a checklist item
DELETE /api/v1/work-items/{id}/checklist/{itemId} Remove a checklist item
POST /api/v1/work-items/{id}/attachments Upload a file (multipart/form-data, field `file`, up to 25 MB). Link it by posting a comment with its id
GET /api/v1/attachments/{id} Redirects to a 5-minute signed URL. Images, PDFs, video, audio and text preview inline; everything else downloads. `?download=1` forces a download
DELETE /api/v1/attachments/{id} Delete a file (uploader or managers)
GET /api/v1/task-views Your saved Tasks views (personal)
POST /api/v1/task-views Save a view: people, agents, projects, clients and labels (any-of; `none` for unset), search and layout
PATCH /api/v1/task-views/{id} Rename, move or update a view's filters
DELETE /api/v1/task-views/{id} Delete a view
GET /api/v1/usage Usage this month and the plan's limit, in bytes (1 GB = 10^9): stored data (database share and files) plus data in and out since the 1st
PUT /api/v1/billing/storage Set extra GB on Pro ($10/GB per month; admins). More is charged pro rata right away
GET /api/v1/chat/channels Your channels and DMs with unread and mention counts. `?browse` adds public channels you haven't joined
POST /api/v1/chat/channels Create a channel (name is slugged; private channels need `memberIds`)
PATCH /api/v1/chat/channels/{id} Rename, set the topic or archive
POST /api/v1/chat/channels/{id}/join Join a public channel
GET /api/v1/search Search the workspace (the ⌘K palette)
GET /api/v1/gifs GIFs for the chat picker (Powered by GIPHY)
PATCH /api/v1/chat/channels/{id}/preferences Your preferences for a channel
POST /api/v1/chat/channels/{id}/leave Leave a channel
POST /api/v1/chat/channels/{id}/members Add people and agents
POST /api/v1/chat/channels/{id}/read Mark the channel read
GET /api/v1/chat/channels/{id}/messages 50 top-level messages, newest last, with `refs`. `?before=` pages back; `?thread=` returns a whole thread
POST /api/v1/chat/channels/{id}/messages Post a message (`parentId` replies in a thread; `attachmentIds` links uploaded files). Mentioning an agent queues a session
POST /api/v1/chat/channels/{id}/files Upload a file (multipart/form-data, field `file`, up to 25 MB), then send its id in `attachmentIds`
GET /api/v1/chat/files/{id} Redirects to a 5-minute signed URL; `?download=1` forces a download
POST /api/v1/chat/dms Open (or reuse) a DM with one or more people and agents
PATCH /api/v1/chat/messages/{id} Edit your message
DELETE /api/v1/chat/messages/{id} Delete a message and its files (author or managers)
POST /api/v1/chat/messages/{id}/reactions Toggle an emoji reaction
POST /api/v1/chat/messages/{id}/task Turn a message into a task; a reply links to it
POST /api/v1/chat/typing Say you're typing (shown for 5 seconds)
GET /api/v1/chat/search Full-text search in messages you can read
GET /api/v1/presence Who's online, what they're tracking (if they share it) and which agents are connected
GET /api/v1/agents Agents in the workspace: provider, model, skills, access, whether they're connected, and canUse (whether you may ask it)
POST /api/v1/agents Add an agent (admins only): name, description, provider, model, skills. Returns its token once
PATCH /api/v1/agents/{id} Rename, describe, set provider, model and skills, set who may ask it (access: everyone or restricted to allowedRoles and allowedUserIds; its owner and admins always may), or turn o
POST /api/v1/work-items/{id}/dependencies Mark this task blocked by another (type blocked_by) or blocking another (type blocks). Refuses itself, loops and more than 50 blockers. A blocked task can't move to in progress, re
DELETE /api/v1/work-items/{id}/dependencies Remove the dependency; a task with no open blockers left unlocks
GET /api/v1/agents/{id}/events The agent's activity log and this month's usage against its budget (owner or admins)
GET /api/v1/agents/{id}/instructions The agent's instructions and every earlier version (owner or admins)
POST /api/v1/agents/{id}/webhook action: test (sends a signed agent.ping and returns the status it got) or rotate (a new signing secret, shown once). Owner or admins
GET /api/v1/agent-requests Requests for new agents: all for admins, your own otherwise
POST /api/v1/agent-requests Ask the admins for a new agent (people and agents)
PATCH /api/v1/agent-requests/{id} Approve (creates the agent, owned by you, without a token) or decline. Admins signed in as themselves
POST /api/v1/agents/{id}/token Replace the agent's token (owner)
GET /api/v1/agent-sessions/{id} One agent session: status, steps, answer, reported tokens and cost, and the helpers it delegated to
POST /api/v1/agent-sessions/{id}/cancel Stop an agent's work and every helper it delegated to (requester or managers)
POST /api/v1/delegations Hand work to an agent: an existing task (workItemId), a new task (title), or a quick request (neither). Agents chain helpers with the MCP tool agent_delegate
POST /api/v1/workspaces Create a workspace
POST /api/v1/workspace/switch Move this signed-in device to another workspace you're a member of, and make it your default. Sign-in sessions only; API and agent tokens stay in their own workspace. Bootstrap lis
GET /api/v1/connected-apps Apps you connected over OAuth (ChatGPT, Claude…). They act as you in your default workspace
DELETE /api/v1/connected-apps/{clientId} Disconnect an app: its tokens stop working at once
POST /api/v1/work-items/{id}/delegate Hand a task to an agent. The people on it stay responsible
GET /api/v1/work-items/{id}/agent-sessions Agent work on a task, with each session's activity
GET /api/v1/activity Your day: tracked entries and what you did (tasks created, moved or commented on, chat messages per channel, agent requests), for drafting a timesheet
GET /api/v1/reports/agents Agent session time (how long agents worked on requests), tokens and reported cost in USD, by agent and project. Time agents log appears in the normal reports as their own rows
POST /api/v1/tasks Create a task type (admins)
PATCH /api/v1/tasks/{id} Update a task type (admins)

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • 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.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hourtick:hourtick-api-v1-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API 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 Specification

hourtick-v1-openapi.json Raw ↑
{"openapi":"3.1.0","info":{"title":"Hourtick 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_…`.\n\nAll 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).\n\nEvery response carries a `Server-Time` header (epoch ms). Errors use RFC 9457 problem details."},"servers":[{"url":"https://hourtick.com"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"ht_… personal API token"}},"schemas":{"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."},"WorkItem":{"type":"object","description":"A task. Status: todo | in_progress | review | done. Priority: 0 none … 4 urgent. `description` (in the detail and in writes) is Markdown, including `- [ ]` checklists.","properties":{"id":{"type":"string","format":"uuid"},"number":{"type":"integer","description":"Short number shown as #12"},"title":{"type":"string"},"status":{"type":"string","enum":["todo","in_progress","review","done"]},"priority":{"type":"integer","minimum":0,"maximum":4},"projectId":{"type":["string","null"]},"clientId":{"type":["string","null"]},"dueDate":{"type":["string","null"],"format":"date"},"estimateSeconds":{"type":["integer","null"]},"labels":{"type":"array","items":{"type":"string"}},"position":{"type":"number"},"assigneeIds":{"type":"array","items":{"type":"string"}},"trackedSeconds":{"type":"integer"},"runningUserIds":{"type":"array","items":{"type":"string"}},"commentCount":{"type":"integer"},"attachmentCount":{"type":"integer"},"checklistDone":{"type":"integer"},"checklistTotal":{"type":"integer"}}},"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"}}}}}}}},"tags":[{"name":"Account","description":"Who you are and what you can track on."},{"name":"Notes and files","description":"Documents and files on a client or a project. Everyone in the workspace can read and add them; the author or a manager deletes. Notes are Tiptap JSON with a plain-text copy (`text`); saves carry a `version` and a stale one answers 409."},{"name":"Time","description":"Timers and time entries. Link time to a task with `workItemId` on start, create or edit."},{"name":"Tasks","description":"The task board: tasks (work items), checklist, chat and files. Any member can create and edit."},{"name":"Integrations","description":"Your own accounts in Linear, Notion, Teamwork and Asana. Connecting needs a person's browser (OAuth); AI agents can't connect or link, and never post through a person's connection. Tokens are stored encrypted and never returned."},{"name":"Reports","description":"Aggregated time and CSV."},{"name":"Billing","description":"Usage (stored data plus data in and out this month) and extra usage. Every plan has unlimited people."},{"name":"Invoices","description":"Invoices through the workspace's own Stripe account (Stripe Connect). An admin connects Stripe in Settings; Stripe Invoicing issues, emails and collects each invoice; Hourtick takes no fee (Stripe's own fees apply). Lines come from billable time not invoiced yet, and sent time is locked as invoiced. Agents send on their own only inside an invoice mandate; otherwise the invoice waits for an admin's approval. Writes take an `idempotencyKey` (body) or `Idempotency-Key` header."},{"name":"Team","description":"Approvals, invoicing and members (role-gated)."},{"name":"Catalog","description":"Clients, projects and task types (admins)."},{"name":"Chat","description":"Channels, DMs, threads, reactions, files and search. Message bodies are Markdown; reference things with links: `[#12 Title](hourtick://task/<id>)`, `hourtick://project/<id>`, `hourtick://client/<id>`, `hourtick://entry/<id>`, and mention with `[@Name](hourtick://user/<id>)` or `hourtick://agent/<id>`. `[@all](hourtick://user/all)` mentions everyone in a channel (not in DMs; managers and admins only beyond 50 people; people can mute it per channel; it never starts agents). Pages come with `refs`: live data for every referenced task, project, client and time entry."},{"name":"Agents","description":"AI agents are workspace members with their own token. Mention one in chat, DM it, or hand it a task, and a session is queued. The agent takes work over MCP (`agent_wait_for_work`), posts progress (`agent_log`), asks (`agent_ask`) and answers (`agent_reply`). It logs the time it worked with `log_time`: billable like anyone's, reported and invoiced as the agent (`userId=agent:<id>` in reports), never on a person's timesheet. Agents don't run timers."}],"paths":{"/api/v1/bootstrap":{"get":{"tags":["Account"],"summary":"Current user, workspace, projects and task types","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"user, workspace, profile, clients, projects (with task types and billable flags), tasks, recent"}}}},"401":{"description":"Missing or invalid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/time-entries":{"get":{"tags":["Time"],"summary":"Your time entries in a date range, plus the running entry","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntry"}},"running":{"oneOf":[{"$ref":"#/components/schemas/TimeEntry"},{"type":"null"}]}}}}}},"401":{"description":"Missing or invalid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"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"}}}}}}},"/api/v1/commands":{"post":{"tags":["Time"],"summary":"Start, stop, continue, create, edit, delete or restore time","description":"`command.type`: `start` (stops any running timer), `restart` (continue an entry on its own date), `stop`, `create` (finished entry with a duration), `edit` (needs `expectedVersion`), `delete`, `restore`. Locked entries return 423; stale versions return 409 with the current entry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"commandId":{"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)$"},"command":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"start"},"entryId":{"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)$"},"spentDate":{"type":"string"},"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)$"},"workItemId":{"anyOf":[{"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)$"},{"type":"null"}]},"notes":{"type":"string","maxLength":5000},"billable":{"type":"boolean"}},"required":["type","entryId","projectId","taskId","notes","billable"]},{"type":"object","properties":{"type":{"type":"string","const":"restart"},"entryId":{"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)$"}},"required":["type","entryId"]},{"type":"object","properties":{"type":{"type":"string","const":"stop"},"entryId":{"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)$"}},"required":["type","entryId"]},{"type":"object","properties":{"type":{"type":"string","const":"edit"},"entryId":{"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)$"},"expectedVersion":{"type":"integer","minimum":1,"maximum":9007199254740991},"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)$"},"workItemId":{"anyOf":[{"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)$"},{"type":"null"}]},"notes":{"type":"string","maxLength":5000},"billable":{"type":"boolean"},"spentDate":{"type":"string"},"durationSeconds":{"type":"integer","minimum":0,"maximum":86400}},"additionalProperties":false}},"required":["type","entryId","expectedVersion","patch"]},{"type":"object","properties":{"type":{"type":"string","const":"delete"},"entryId":{"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)$"},"expectedVersion":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["type","entryId","expectedVersion"]},{"type":"object","properties":{"type":{"type":"string","const":"restore"},"entryId":{"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)$"}},"required":["type","entryId"]},{"type":"object","properties":{"type":{"type":"string","const":"create"},"entryId":{"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)$"},"spentDate":{"type":"string"},"durationSeconds":{"type":"integer","minimum":0,"maximum":86400},"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)$"},"workItemId":{"anyOf":[{"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)$"},{"type":"null"}]},"notes":{"type":"string","maxLength":5000},"billable":{"type":"boolean"}},"required":["type","entryId","spentDate","durationSeconds","projectId","taskId","notes","billable"]}]},"clientTime":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["commandId","command"]},"example":{"commandId":"0192f1a0-7c1e-7cc0-9b8e-2f5f0b0a1c11","command":{"type":"start","entryId":"0192f1a0-7c1e-7cc0-9b8e-2f5f0b0a1c12","projectId":"<project uuid>","taskId":"<task uuid>","notes":"Homepage copy","billable":true}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntry"}},"noop":{"type":"boolean"}}}}}},"401":{"description":"Missing or invalid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Version conflict (response includes the current entry)"},"422":{"description":"Invalid request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"423":{"description":"Entry or day is locked (invoiced, submitted or approved)"}}}},"/api/v1/reports":{"get":{"tags":["Reports"],"summary":"Totals, groups and entries for a period (team scope for managers). For admins, totals and groups also carry the P&L: `peopleCostMinor` (hours × each person's cost rate on that day), `agentCostMinor` (agents' reported cost, converted from USD at ECB rates), `costMinor`, `profitMinor` (billable amount − cost) and `uncostedSeconds`; `pnl` gives the currency","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"groupBy","in":"query","schema":{"type":"string","enum":["client","project","task","user","work_item"],"default":"project"},"description":"`task` groups by task type, `work_item` by task"},{"name":"userId","in":"query","schema":{"type":"string"}},{"name":"clientId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"projectId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"taskId","in":"query","schema":{"type":"string","format":"uuid"},"description":"Task type"},{"name":"workItemId","in":"query","schema":{"type":"string"},"description":"Task id, or `none` for time without a task"},{"name":"billable","in":"query","schema":{"type":"string","enum":["true","false"]}},{"name":"invoiced","in":"query","schema":{"type":"string","enum":["true","false"]}},{"name":"format","in":"query","schema":{"type":"string","enum":["csv"]},"description":"Return CSV instead of JSON"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"scope, totals, groups, entries, truncated"}}}},"401":{"description":"Missing or invalid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/stream":{"get":{"tags":["Time"],"summary":"Server-Sent Events: live entry changes for your account and task changes in the workspace","responses":{"200":{"description":"text/event-stream with `hello`, `entries`, `work_items` (`workItemId`), `chat` (`channelId`, `messageId`, `parentId`, `change`: message, edit, delete, reaction, read, channel, typing or agent) and `presence` events. Private channels and DMs only reach their members"}}}},"/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"}}}}}},"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"}}}},"/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"}}},"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"}}}}}}},"/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"}}}}}}}}},"/api/v1/members":{"get":{"tags":["Team"],"summary":"Workspace members (managers)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/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"}}}},"/api/v1/members/{userId}/cost-rate":{"put":{"tags":["Team"],"summary":"Set what an hour of a member costs, from a day on (admins). Effective-dated: time is costed at the rate valid on its day","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"}}}},"/api/v1/agent-costs":{"get":{"tags":["Agents"],"summary":"Agent cost ledger for a period: an agent's own rows, or all for admins (`agentId` narrows it)","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"agentId","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Agents"],"summary":"Report what a piece of work cost (agent token: its own; admins: give agentId). Counts toward the agent's budget and the P&L. Report each cost once (here, MCP log_time usage, or session usage)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"costUsd":{"description":"What the work cost in US dollars (model and tool spend)","type":"number","minimum":0,"maximum":10000},"model":{"type":"string","maxLength":80},"inputTokens":{"type":"integer","minimum":0,"maximum":9007199254740991},"outputTokens":{"type":"integer","minimum":0,"maximum":9007199254740991},"agentId":{"description":"Admins only: the agent the cost is for. Agents always report their own","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)$"},"workItemId":{"description":"The task the cost belongs to; its project is used","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)$"},"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":{"description":"Task type","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)$"},"timeEntryId":{"description":"One of the agent's time entries; its project, task type, task and day are used","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)$"},"spentDate":{"description":"Defaults to today","type":"string"},"note":{"type":"string","maxLength":2000}}}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"403":{"description":"Not the agent or an admin, or outside the agent's scope"},"422":{"description":"Invalid request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/ai-providers":{"get":{"tags":["Agents"],"summary":"AI provider connections for verified agent cost (admins). Keys are never returned, only their last 4 characters","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"{ connections }"}}}}}},"post":{"tags":["Agents"],"summary":"Connect Anthropic (Admin API key), OpenAI (admin key) or OpenRouter (management key) to read what they billed (admins). The key is checked against the provider, then stored encrypted","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"provider":{"type":"string","enum":["anthropic","openai","openrouter"]},"key":{"type":"string","minLength":8,"maxLength":500,"description":"Anthropic Admin API key, OpenAI admin key, or OpenRouter management key. Stored encrypted, never returned"},"label":{"type":"string","maxLength":80},"fillGaps":{"description":"Add what the provider billed beyond what agents reported to the cost ledger (default true)","type":"boolean"}},"required":["provider","key"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"422":{"description":"The provider rejected the key"},"503":{"description":"INTEGRATIONS_ENCRYPTION_KEY not set"}}}},"/api/v1/ai-providers/{id}":{"patch":{"tags":["Agents"],"summary":"Rename, or turn filling in from bills on or off (admins)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"label":{"type":"string","maxLength":80},"fillGaps":{"type":"boolean"}}}}}},"responses":{"204":{"description":"Saved"}}},"delete":{"tags":["Agents"],"summary":"Remove the key, its synced bills and the cost it filled in (admins)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"}}}},"/api/v1/ai-providers/{id}/scopes":{"get":{"tags":["Agents"],"summary":"What the key can see (Anthropic workspaces, OpenAI projects and API keys, OpenRouter keys) and which agent each is mapped to (admins)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"put":{"tags":["Agents"],"summary":"Map a scope to an agent, or clear it with agentId null. A scope belongs to one agent (admins)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"scopeType":{"type":"string","enum":["workspace","project","api_key","key"]},"scopeId":{"type":"string","minLength":1,"maxLength":200},"name":{"type":"string","maxLength":200},"agentId":{"anyOf":[{"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)$"},{"type":"null"}],"description":"The agent this bill belongs to; null removes the mapping"}},"required":["scopeType","scopeId","agentId"]}}}},"responses":{"204":{"description":"Saved"},"409":{"description":"OpenAI: an agent maps to projects or API keys, not both"}}}},"/api/v1/ai-providers/{id}/sync":{"post":{"tags":["Agents"],"summary":"Read the last 30 days of bills now and reconcile (admins). Runs every 6 hours on its own","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"integer"}}}}}}}}},"/api/v1/reports/cost-reconciliation":{"get":{"tags":["Reports","Agents"],"summary":"Agent cost as reported vs as billed by the providers, per agent and UTC month, with what was filled in (source `provider` in the cost ledger) (admins)","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/reports/agent-outcomes":{"get":{"tags":["Reports","Agents"],"summary":"Agent outcomes: tasks with agent work done in the period, with the agent's time and cost plus people's time before (briefing) and after (review and rework) the agent delivered, how often work was sent back, and who approved it. Admins also get money (full cost, revenue, margin); managers hours; an agent only its own tasks","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"agentId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"projectId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"clientId","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"{ currency, from, to, totals, agents[], tasks[] }"}}}},"403":{"description":"Managers, admins and agents"}}}},"/api/v1/client-ai-reports/preview":{"get":{"tags":["Reports"],"summary":"Client AI work report without sharing it: people's and agents' hours, agents and models, and whether a person reviewed each piece of agent work. No rates, costs or notes (managers)","parameters":[{"name":"clientId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"projectId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"namePeople","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK

# --- truncated at 32 KB (96 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/hourtick/refs/heads/main/openapi/_original/hourtick-v1-openapi.json