{"openapi":"3.1.0","info":{"title":"Hourdini REST API","version":"1.0.0","description":"HTTP API for Hourdini's time-tracking, client, and invoicing surface.\n\n## Authentication\n\nSend a Personal Access Token as a bearer credential:\n`Authorization: Bearer hd_pat_...`. PATs are minted at `/cli/connect`\nand are bound to one user and one organisation.\n\n## Money fields\n\nEvery monetary value (any field suffixed `_minor`, `_amount`, or `_total`) is\nserialized as a JSON **string** of integer minor units in the field's currency.\nThis preserves bigint precision past `Number.MAX_SAFE_INTEGER`. Parse with\n`BigInt(...)` before any arithmetic.\n\n## No currency conversion\n\nMulti-currency totals are reported per native currency side-by-side.\nThere is no reporting currency, no FX rates, and no conversion endpoint.\n\n## Pagination envelope\n\nPaginated list endpoints return:\n\n```json\n{ \"<resource_plural>\": [...], \"pagination\": { \"limit\": 100, \"offset\": 0, \"total\": 0, \"has_more\": false } }\n```\n\n## PATCH null-vs-omit\n\nOmitting a field leaves it alone. Sending `null` clears nullable fields.\nThe two are distinct on every PATCH route.\n\n## Error envelope\n\nEvery 4xx/5xx response has the shape `{ error: { code, message, details?, request_id? } }`.\n`code` is a stable snake_case machine string. `message` is human-readable."},"servers":[{"url":"/","description":"Current host"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Identity"},{"name":"Organisations"},{"name":"Members"},{"name":"Invites"},{"name":"Clients"},{"name":"Projects"},{"name":"Tags"},{"name":"Time Entries"},{"name":"Timer"},{"name":"Invoices"},{"name":"Budgets"},{"name":"Overlaps"},{"name":"Reports"},{"name":"Search"},{"name":"Exports"},{"name":"Meta"}],"paths":{"/api/v1/me":{"get":{"operationId":"get_me","summary":"Identity probe for the authenticated principal.","tags":["Identity"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"description":"Returns the user and the resolved active organisation. CLI/PAT clients use this to discover the org the token is bound to."}},"/api/v1/orgs":{"get":{"operationId":"get_orgs","summary":"List organisations the caller belongs to.","tags":["Organisations"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_orgs","summary":"Create a new organisation.","tags":["Organisations"],"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"description":"Capped at two organisations per user during the public beta.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":63}},"required":["name"]}}}}}},"/api/v1/orgs/{orgId}":{"get":{"operationId":"get_orgs_by_orgId","summary":"Get the active organisation.","tags":["Organisations"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"patch_orgs_by_orgId","summary":"Update organisation profile / billing details.","tags":["Organisations"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"legal_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"legal_address":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"vat_number":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}]},"payment_instructions":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]}}}}}}},"delete":{"operationId":"delete_orgs_by_orgId","summary":"Delete the organisation. Owner-only.","tags":["Organisations"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orgs/{orgId}/members":{"get":{"operationId":"get_orgs_by_orgId_members","summary":"List organisation members.","tags":["Members"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orgs/{orgId}/members/{userId}":{"patch":{"operationId":"patch_orgs_by_orgId_members_by_userId","summary":"Update a member's role. Owner-only.","tags":["Members"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["owner","member","viewer"]}},"required":["role"]}}}}},"delete":{"operationId":"delete_orgs_by_orgId_members_by_userId","summary":"Remove a member from the organisation.","tags":["Members"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orgs/{orgId}/invites":{"get":{"operationId":"get_orgs_by_orgId_invites","summary":"List pending organisation invites. Owner-only.","tags":["Invites"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_orgs_by_orgId_invites","summary":"Invite a user to the organisation. Owner-only.","tags":["Invites"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","minLength":3,"maxLength":254},"role":{"type":"string","enum":["owner","member","viewer"]},"client_ids":{"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)$"}}},"required":["email","role"]}}}}}},"/api/v1/orgs/{orgId}/invites/{inviteId}":{"delete":{"operationId":"delete_orgs_by_orgId_invites_by_inviteId","summary":"Revoke a pending invite. Owner-only.","tags":["Invites"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"inviteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/orgs/{orgId}/clients/{clientId}/grants":{"post":{"operationId":"post_orgs_by_orgId_clients_by_clientId_grants","summary":"Grant a viewer scoped access to a single client. Owner-only.","tags":["Members"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_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)$"}},"required":["user_id"]}}}}}},"/api/v1/orgs/{orgId}/clients/{clientId}/grants/{userId}":{"delete":{"operationId":"delete_orgs_by_orgId_clients_by_clientId_grants_by_userId","summary":"Revoke a viewer's per-client grant. Owner-only.","tags":["Members"],"parameters":[{"name":"orgId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/invites":{"get":{"operationId":"get_invites","summary":"List pending invites addressed to the caller.","tags":["Invites"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/invites/{inviteId}/respond":{"post":{"operationId":"post_invites_by_inviteId_respond","summary":"Accept or decline an invite addressed to the caller.","tags":["Invites"],"parameters":[{"name":"inviteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accept":{"type":"boolean"}},"required":["accept"]}}}}}},"/api/v1/clients":{"get":{"operationId":"get_clients","summary":"List clients in the active organisation.","tags":["Clients"],"parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_clients","summary":"Create a client.","tags":["Clients"],"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"default_currency":{"type":"string","pattern":"^[A-Z]{3}$"},"default_rate_minor":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"string","pattern":"^\\d+$"}]},"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}]},"billing_address":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"color":{"type":"integer","minimum":1,"maximum":12}},"required":["name","default_currency","default_rate_minor"]}}}}}},"/api/v1/clients/{id}":{"get":{"operationId":"get_clients_by_id","summary":"Get a single client.","tags":["Clients"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"patch_clients_by_id","summary":"Update a client.","tags":["Clients"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"description":"Patch semantics: omitting a field leaves it alone; sending `null` clears nullable fields.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"default_currency":{"type":"string","pattern":"^[A-Z]{3}$"},"default_rate_minor":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"string","pattern":"^\\d+$"}]},"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}]},"billing_address":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"color":{"anyOf":[{"type":"integer","minimum":1,"maximum":12},{"type":"null"}]}}}}}}},"delete":{"operationId":"delete_clients_by_id","summary":"Archive a client. Idempotent.","tags":["Clients"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/clients/{id}/restore":{"post":{"operationId":"post_clients_by_id_restore","summary":"Restore an archived client. Idempotent.","tags":["Clients"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/projects":{"get":{"operationId":"get_projects","summary":"List projects.","tags":["Projects"],"parameters":[{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"include_archived","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_projects","summary":"Create a project.","tags":["Projects"],"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_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)$"},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}]},"rate_minor":{"anyOf":[{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"string","pattern":"^\\d+$"}]},{"type":"null"}]},"color":{"anyOf":[{"type":"integer","minimum":1,"maximum":12},{"type":"null"}]}},"required":["client_id","name"]}}}}}},"/api/v1/projects/{id}":{"get":{"operationId":"get_projects_by_id","summary":"Get a project.","tags":["Projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"patch_projects_by_id","summary":"Update a project.","tags":["Projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}]},"rate_minor":{"anyOf":[{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"string","pattern":"^\\d+$"}]},{"type":"null"}]},"color":{"anyOf":[{"type":"integer","minimum":1,"maximum":12},{"type":"null"}]},"client_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)$"},"bill_agent_time":{"type":"boolean"}}}}}}},"delete":{"operationId":"delete_projects_by_id","summary":"Archive a project. Idempotent.","tags":["Projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/projects/{id}/restore":{"post":{"operationId":"post_projects_by_id_restore","summary":"Restore an archived project. Idempotent.","tags":["Projects"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/projects/{id}/budget":{"get":{"operationId":"get_projects_by_id_budget","summary":"Get the budget configured for a project (null if none).","tags":["Budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"put_projects_by_id_budget","summary":"Create or replace a project's budget.","tags":["Budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string","enum":["lifetime","monthly","quarterly","yearly"]},"hours_limit":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"amount_minor_limit":{"anyOf":[{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"string","pattern":"^\\d+$"}]},{"type":"null"}]},"amount_currency":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}]},"basis":{"type":"string","enum":["billable","all"]},"threshold_warn":{"type":"integer","minimum":1,"maximum":100},"threshold_critical":{"type":"integer","minimum":1,"maximum":200}},"required":["period"]}}}}},"delete":{"operationId":"delete_projects_by_id_budget","summary":"Remove a project's budget.","tags":["Budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/projects/{id}/budget/usage":{"get":{"operationId":"get_projects_by_id_budget_usage","summary":"Get current usage for a project's budget.","tags":["Budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/tags":{"get":{"operationId":"get_tags","summary":"List tags.","tags":["Tags"],"parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_tags","summary":"Create a tag.","tags":["Tags"],"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"color":{"anyOf":[{"type":"integer","minimum":1,"maximum":12},{"type":"null"}]}},"required":["name"]}}}}}},"/api/v1/tags/{id}":{"get":{"operationId":"get_tags_by_id","summary":"Get a tag.","tags":["Tags"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"patch_tags_by_id","summary":"Update a tag.","tags":["Tags"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"color":{"anyOf":[{"type":"integer","minimum":1,"maximum":12},{"type":"null"}]}}}}}}},"delete":{"operationId":"delete_tags_by_id","summary":"Archive a tag. Idempotent.","tags":["Tags"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/tags/{id}/restore":{"post":{"operationId":"post_tags_by_id_restore","summary":"Restore an archived tag. Idempotent.","tags":["Tags"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/time-entries":{"get":{"operationId":"get_time_entries","summary":"List time entries (paginated).","tags":["Time Entries"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"default":100,"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"project_id","in":"query","required":false,"schema":{"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)$"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"billable","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}},{"name":"billed","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}},{"name":"running","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}},{"name":"tag_id","in":"query","required":false,"schema":{"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":"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)$"}}]}},{"name":"q","in":"query","required":false,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_time_entries","summary":"Log a closed time entry with an explicit duration.","tags":["Time Entries"],"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"project_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)$"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"duration_seconds":{"type":"integer","exclusiveMinimum":0,"maximum":2592000},"started_at":{"type":"string"},"billable":{"type":"boolean"},"tag_ids":{"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)$"}},"links":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":2000}}},"required":["project_id","duration_seconds"]}}}}}},"/api/v1/time-entries/{id}":{"get":{"operationId":"get_time_entries_by_id","summary":"Get a time entry.","tags":["Time Entries"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"patch_time_entries_by_id","summary":"Edit a time entry. Blocked when the entry is on a sent invoice.","tags":["Time Entries"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"started_at":{"type":"string"},"ended_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"billable":{"type":"boolean"},"tag_ids":{"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)$"}},"links":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":2000}}}}}}}},"delete":{"operationId":"delete_time_entries_by_id","summary":"Delete a time entry. Blocked when the entry is on a sent invoice.","tags":["Time Entries"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/time-entries/summary":{"get":{"operationId":"get_time_entries_summary","summary":"Aggregated totals over a filtered range, per-currency.","tags":["Time Entries"],"parameters":[{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"project_id","in":"query","required":false,"schema":{"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)$"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"billable","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}},{"name":"billed","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}},{"name":"tag_id","in":"query","required":false,"schema":{"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":"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)$"}}]}},{"name":"q","in":"query","required":false,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/timer":{"get":{"operationId":"get_timer","summary":"Inspect the running timer for the authenticated user (null when none).","tags":["Timer"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"put_timer","summary":"Start a timer. Conflicts (409) if one is already running.","tags":["Timer"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"project_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)$"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"started_at":{"type":"string"},"tag_ids":{"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)$"}},"links":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":2000}}},"required":["project_id"]}}}}},"delete":{"operationId":"delete_timer","summary":"Stop the running timer. 204 if none was running.","tags":["Timer"],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/invoices":{"get":{"operationId":"get_invoices","summary":"List invoices.","tags":["Invoices"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"default":100,"type":"integer","minimum":1,"maximum":500}},{"name":"offset","in":"query","required":false,"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","sent","paid","void"]}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"post_invoices","summary":"Create a draft invoice from a period of unbilled time entries.","tags":["Invoices"],"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_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)$"},"period":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"object","properties":{"from":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"to":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},"required":["from","to"]}]},"line_item_mode":{"type":"string","enum":["summary","project","time_entry"]},"tax_rate":{"type":"number","minimum":0,"maximum":1},"due_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]}},"required":["client_id","period"]}}}}}},"/api/v1/invoices/{id}":{"get":{"operationId":"get_invoices_by_id","summary":"Get an invoice (including line items).","tags":["Invoices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"patch_invoices_by_id","summary":"Update a draft invoice. Sent or paid invoices are immutable.","tags":["Invoices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tax_rate":{"type":"number","minimum":0,"maximum":1},"due_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"line_items":{"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)$"},"label":{"type":"string","minLength":1,"maxLength":200},"detail":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"duration_seconds":{"type":"integer","exclusiveMinimum":0,"maximum":2592000},"rate_minor":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"string","pattern":"^\\d+$"}]},"project_id":{"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"}]}},"required":["label","duration_seconds","rate_minor"]}}}}}}}}},"/api/v1/invoices/{id}/pdf":{"get":{"operationId":"get_invoices_by_id_pdf","summary":"Download an invoice PDF.","tags":["Invoices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"PDF download.","content":{"application/pdf":{"schema":{"type":"string","contentEncoding":"base64"}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/invoices/{id}/send":{"post":{"operationId":"post_invoices_by_id_send","summary":"Send a draft invoice. Snapshots its line items immutably.","tags":["Invoices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/invoices/{id}/mark-paid":{"post":{"operationId":"post_invoices_by_id_mark_paid","summary":"Mark a sent invoice as paid.","tags":["Invoices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}]},"reference":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"paid_at":{"type":"string"}}}}}}}},"/api/v1/invoices/{id}/void":{"post":{"operationId":"post_invoices_by_id_void","summary":"Void an invoice.","tags":["Invoices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/overlaps":{"get":{"operationId":"get_overlaps","summary":"List time-entry overlaps for the same client across users.","tags":["Overlaps"],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"invoice_id","in":"query","required":false,"schema":{"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)$"}},{"name":"acknowledged","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/overlaps/acknowledgements":{"post":{"operationId":"post_overlaps_acknowledgements","summary":"Acknowledge an overlap pair.","tags":["Overlaps"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"entry_a_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)$"},"entry_b_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)$"}},"required":["entry_a_id","entry_b_id"]}}}}},"delete":{"operationId":"delete_overlaps_acknowledgements","summary":"Remove an acknowledgement.","tags":["Overlaps"],"parameters":[{"name":"entry_a_id","in":"query","required":true,"schema":{"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)$"}},{"name":"entry_b_id","in":"query","required":true,"schema":{"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)$"}}],"responses":{"204":{"description":"Deleted (or already absent)."},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/reports/utilization":{"get":{"operationId":"get_reports_utilization","summary":"Utilization report grouped by user, project, or client.","tags":["Reports"],"parameters":[{"name":"period","in":"query","required":false,"schema":{"default":"week","type":"string","minLength":1,"maxLength":120}},{"name":"group_by","in":"query","required":false,"schema":{"default":"user","type":"string","enum":["user","project","client"]}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"project_id","in":"query","required":false,"schema":{"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)$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/search":{"get":{"operationId":"get_search","summary":"Fuzzy resolve clients or projects by name.","tags":["Search"],"parameters":[{"name":"type","in":"query","required":true,"schema":{"type":"string","enum":["client","project"]}},{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":200}},{"name":"limit","in":"query","required":false,"schema":{"default":3,"type":"integer","minimum":1,"maximum":20}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/exports/time-entries.csv":{"get":{"operationId":"get_exports_time_entries_csv","summary":"Download time entries as a CSV file.","tags":["Exports"],"parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"project_id","in":"query","required":false,"schema":{"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)$"}},{"name":"billable","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}},{"name":"tag_id","in":"query","required":false,"schema":{"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":"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)$"}}]}}],"responses":{"200":{"description":"CSV download.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/exports/activity.csv":{"get":{"operationId":"get_exports_activity_csv","summary":"Download the audit activity log as a CSV file.","tags":["Exports"],"parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["web","cli","mcp","system"]}},{"name":"group","in":"query","required":false,"schema":{"type":"string","enum":["time","invoice","client","project","tag","auth","admin","system"]}},{"name":"client_id","in":"query","required":false,"schema":{"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)$"}},{"name":"include_system","in":"query","required":false,"schema":{"anyOf":[{"type":"string","const":"true"},{"type":"string","const":"false"},{"type":"boolean"}]}}],"responses":{"200":{"description":"CSV download.","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/openapi.json":{"get":{"operationId":"get_openapi_json","summary":"This OpenAPI 3.1 document.","tags":["Meta"],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Caller is authenticated but not authorized for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Resource not found in the caller's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"State transition conflict.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Semantically invalid request (e.g. mixed currencies on an invoice).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"security":[]}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"PAT"}},"schemas":{"ApiError":{"type":"object","required":["error"],"additionalProperties":false,"properties":{"error":{"type":"object","required":["code","message"],"additionalProperties":false,"properties":{"code":{"type":"string","description":"Stable snake_case machine code, e.g. `not_found` or `validation_failed`."},"message":{"type":"string","description":"Human-readable message safe to surface to end users."},"details":{"type":"object","additionalProperties":true,"description":"Optional context (zod issues, conflict context, etc.). Never contains stack traces."},"request_id":{"type":"string","description":"Echo of the `x-request-id` header for log correlation."}}}}},"Pagination":{"type":"object","required":["limit","offset","total","has_more"],"additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1},"offset":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0},"has_more":{"type":"boolean"}}}},"responses":{}}}