PSA/ee/docs/api-registry/tickets.json
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

720 lines
34 KiB
JSON

{
"entries": [
{
"match": {
"method": "get",
"path": "/api/v1/tickets"
},
"metadata": {
"displayName": "List Tickets",
"summary": "List tickets",
"description": "Returns a paginated list of tickets for the current tenant. Supports filtering by board, status, priority, client, assignee, category, open/closed/overdue flags, and entered/closed date ranges — see the parameters list for all available filters. For aggregate counts (e.g. how many tickets are open or high priority), prefer GET /api/v1/tickets/stats or set limit=1 and read pagination.total from the response. Use GET /api/v1/boards, GET /api/v1/statuses, and GET /api/v1/priorities for lookup data instead of sampling tickets. If you send the fields query parameter, use only these exact field names: ticket_id, ticket_number, title, status_id, status_name, status_is_closed, priority_name, assigned_to_name, client_name, contact_name, updated_at, entered_at, closed_at, tags, or mobile_list. Do not invent aliases such as id, subject, status, priority, client, created_at, or description.",
"rbacResource": "ticket",
"approvalRequired": false,
"parameters": [
{
"name": "page",
"in": "query",
"required": false,
"description": "Pagination page number (default 1).",
"schema": { "type": "integer", "minimum": 1 }
},
{
"name": "limit",
"in": "query",
"required": false,
"description": "Number of tickets per page (max 100, default 25). Set limit=1 when you only need pagination.total for an aggregate count.",
"schema": { "type": "integer", "minimum": 1, "maximum": 100 }
},
{
"name": "board_id",
"in": "query",
"required": false,
"description": "Filter to a specific board.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "board_name",
"in": "query",
"required": false,
"description": "Filter by board display name (case-sensitive exact match). Prefer board_id when you already have a UUID.",
"schema": { "type": "string" }
},
{
"name": "status_id",
"in": "query",
"required": false,
"description": "Filter by a single ticket status UUID.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "status_ids",
"in": "query",
"required": false,
"description": "Filter by multiple status UUIDs as a comma-separated list (for example status_ids=uuid1,uuid2). Useful for matching several open or closed statuses at once.",
"schema": { "type": "string" }
},
{
"name": "status_name",
"in": "query",
"required": false,
"description": "Filter by status display name (case-sensitive exact match). Prefer status_id/status_ids when you already have UUIDs.",
"schema": { "type": "string" }
},
{
"name": "priority_id",
"in": "query",
"required": false,
"description": "Filter by ticket priority UUID. Call GET /api/v1/priorities to discover priority UUIDs.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "priority_name",
"in": "query",
"required": false,
"description": "Filter by priority display name (case-sensitive exact match). Use this when the user asks for tickets of a specific priority like \"High\", \"Highest\", \"Medium\", or \"Low\" and you do not want to look up a UUID first.",
"schema": { "type": "string" }
},
{
"name": "category_id",
"in": "query",
"required": false,
"description": "Filter by ticket category UUID.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "subcategory_id",
"in": "query",
"required": false,
"description": "Filter by ticket subcategory UUID.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "category_name",
"in": "query",
"required": false,
"description": "Filter by category display name (case-sensitive exact match).",
"schema": { "type": "string" }
},
{
"name": "client_id",
"in": "query",
"required": false,
"description": "Filter by client identifier.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "client_name",
"in": "query",
"required": false,
"description": "Filter by client display name (case-sensitive exact match). Prefer client_id when you already have a UUID.",
"schema": { "type": "string" }
},
{
"name": "location_id",
"in": "query",
"required": false,
"description": "Filter by client location UUID.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "contact_name_id",
"in": "query",
"required": false,
"description": "Filter by primary contact UUID on the ticket.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "contact_name",
"in": "query",
"required": false,
"description": "Filter by contact display name (case-sensitive exact match).",
"schema": { "type": "string" }
},
{
"name": "assigned_to",
"in": "query",
"required": false,
"description": "Filter tickets assigned to a specific technician (user UUID).",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "entered_by",
"in": "query",
"required": false,
"description": "Filter tickets created by a specific user UUID.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "title",
"in": "query",
"required": false,
"description": "Filter by ticket title (substring match).",
"schema": { "type": "string" }
},
{
"name": "ticket_number",
"in": "query",
"required": false,
"description": "Filter by ticket number (for example alga0001748).",
"schema": { "type": "string" }
},
{
"name": "is_open",
"in": "query",
"required": false,
"description": "When true, only open tickets are returned.",
"schema": { "type": "boolean" }
},
{
"name": "is_closed",
"in": "query",
"required": false,
"description": "When true, only closed tickets are returned.",
"schema": { "type": "boolean" }
},
{
"name": "has_assignment",
"in": "query",
"required": false,
"description": "When true, only tickets that have an assignee are returned; when false, only unassigned tickets.",
"schema": { "type": "boolean" }
},
{
"name": "entered_from",
"in": "query",
"required": false,
"description": "Lower bound on entered_at as an ISO-8601 datetime (inclusive).",
"schema": { "type": "string", "format": "date-time" }
},
{
"name": "entered_to",
"in": "query",
"required": false,
"description": "Upper bound on entered_at as an ISO-8601 datetime (inclusive).",
"schema": { "type": "string", "format": "date-time" }
},
{
"name": "closed_from",
"in": "query",
"required": false,
"description": "Lower bound on closed_at as an ISO-8601 datetime (inclusive).",
"schema": { "type": "string", "format": "date-time" }
},
{
"name": "closed_to",
"in": "query",
"required": false,
"description": "Upper bound on closed_at as an ISO-8601 datetime (inclusive).",
"schema": { "type": "string", "format": "date-time" }
},
{
"name": "fields",
"in": "query",
"required": false,
"description": "Comma-separated list of fields to return. Use only these exact values: ticket_id, ticket_number, title, status_id, status_name, status_is_closed, priority_name, assigned_to_name, client_name, contact_name, updated_at, entered_at, closed_at, tags, or mobile_list. If you are not sure, omit fields entirely instead of guessing aliases.",
"schema": { "type": "string" }
},
{
"name": "field_ranges[description_html]",
"in": "query",
"required": false,
"description": "Optional UTF-8 byte range for the description_html field, formatted as start-end (for example 0-4095). When returned data is truncated, inspect meta.truncated_fields for continuation offsets.",
"schema": { "type": "string", "pattern": "^\\d+-\\d+$" }
}
],
"responseBodySchema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ticket_id": { "type": "string", "format": "uuid" },
"ticket_number": { "type": "string" },
"title": { "type": "string" },
"client_id": { "type": "string", "format": "uuid" },
"board_id": { "type": "string", "format": "uuid" },
"status_id": { "type": "string", "format": "uuid" },
"priority_id": { "type": "string", "format": "uuid" },
"assigned_to": { "type": ["string", "null"], "format": "uuid" },
"entered_at": { "type": "string", "format": "date-time" }
},
"required": [
"ticket_id",
"ticket_number",
"title",
"client_id",
"board_id",
"status_id",
"priority_id",
"entered_at"
]
}
},
"pagination": {
"type": "object",
"properties": {
"page": { "type": "integer" },
"limit": { "type": "integer" },
"total": { "type": "integer" },
"totalPages": { "type": "integer" },
"hasNext": { "type": "boolean" },
"hasPrev": { "type": "boolean" }
}
}
}
},
"examples": [
{
"name": "Sample recent tickets for reference data",
"request": {
"query": {
"limit": 5,
"fields": "ticket_id,ticket_number,title,board_id,status_id,priority_id,assigned_to,entered_at",
"is_open": true
}
},
"notes": "Inspect the response payload to reuse board_id, status_id, priority_id, and assigned_to values that are known to be valid. Prefer fields=... to keep payloads compact, but only use the documented field names exactly as written. If you are unsure which field names are valid, omit fields instead of guessing. If you only need a single set of identifiers, take the first ticket in the data array that has non-null board_id, status_id, and priority_id and reuse those UUIDs for the new ticket."
}
]
}
},
{
"match": {
"method": "get",
"path": "/api/v1/tickets/stats"
},
"metadata": {
"displayName": "Get Ticket Stats",
"summary": "Aggregate ticket counts and metrics",
"description": "Returns pre-computed ticket counts and metrics for the current tenant in a single call. Use this whenever the user asks aggregate questions like \"how many tickets are open\", \"how many high priority tickets\", \"how many overdue tickets\", or \"how many tickets per board/status/priority/category\". This endpoint returns totals grouped by status, priority, category, and board, plus counts of open/closed/overdue/unassigned tickets and tickets created today/this week/this month. Prefer this over paginating GET /api/v1/tickets and counting rows yourself.",
"rbacResource": "ticket",
"approvalRequired": false,
"parameters": [],
"responseBodySchema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"description": "Ticket statistics for the current tenant. Priority names (e.g. \"High\", \"Medium\") are the keys in tickets_by_priority and map directly to the priority_name values used by GET /api/v1/tickets.",
"properties": {
"total_tickets": {
"type": "integer",
"description": "Total number of tickets in the tenant, regardless of status."
},
"open_tickets": {
"type": "integer",
"description": "Number of tickets that are currently open (status.is_closed = false)."
},
"closed_tickets": {
"type": "integer",
"description": "Number of tickets whose status is marked closed."
},
"overdue_tickets": {
"type": "integer",
"description": "Number of open tickets past their due/SLA date."
},
"unassigned_tickets": {
"type": "integer",
"description": "Number of open tickets with no assignee."
},
"tickets_by_status": {
"type": "object",
"additionalProperties": { "type": "integer" },
"description": "Map of status display name to ticket count, for example {\"New\": 42, \"In Progress\": 18, \"Closed\": 140}."
},
"tickets_by_priority": {
"type": "object",
"additionalProperties": { "type": "integer" },
"description": "Map of priority display name to ticket count, for example {\"High\": 12, \"Highest\": 3, \"Medium\": 55, \"Low\": 20}. These keys are the same strings accepted by the priority_name filter on GET /api/v1/tickets."
},
"tickets_by_category": {
"type": "object",
"additionalProperties": { "type": "integer" },
"description": "Map of category display name to ticket count."
},
"tickets_by_board": {
"type": "object",
"additionalProperties": { "type": "integer" },
"description": "Map of board display name to ticket count."
},
"average_resolution_time": {
"type": ["number", "null"],
"description": "Average resolution time in hours across closed tickets, or null if there are no closed tickets."
},
"tickets_created_today": { "type": "integer" },
"tickets_created_this_week": { "type": "integer" },
"tickets_created_this_month": { "type": "integer" }
},
"required": [
"total_tickets",
"open_tickets",
"closed_tickets",
"overdue_tickets",
"unassigned_tickets",
"tickets_by_status",
"tickets_by_priority",
"tickets_by_category",
"tickets_by_board",
"tickets_created_today",
"tickets_created_this_week",
"tickets_created_this_month"
]
}
}
},
"examples": [
{
"name": "Answer \"how many high priority tickets are open\" in one call",
"request": {},
"notes": "Call this endpoint and read data.tickets_by_priority[\"High\"] for the count of high-priority tickets. Note the map covers all tickets regardless of status — if the user asks specifically about OPEN high-priority tickets and you need to scope by status, fall back to GET /api/v1/tickets?is_open=true&priority_name=High&limit=1 and read pagination.total from that response."
}
]
}
},
{
"match": {
"method": "get",
"path": "/api/v1/tickets/{id}"
},
"metadata": {
"displayName": "Get Ticket",
"summary": "Get ticket by ID",
"description": "Returns the full ticket record for a specific ticket. Prefer this over expanding list responses when you already know the ticket_id.",
"rbacResource": "ticket",
"approvalRequired": false,
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Ticket identifier.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "field_ranges[description_html]",
"in": "query",
"required": false,
"description": "Optional UTF-8 byte range for description_html, formatted as start-end (for example 0-4095). When returned data is truncated, inspect meta.truncated_fields for continuation offsets.",
"schema": { "type": "string", "pattern": "^\\d+-\\d+$" }
}
],
"examples": [
{
"name": "Read one ticket in detail",
"request": {
"params": {
"id": "11111111-1111-1111-1111-111111111111"
}
},
"notes": "Use this endpoint after a list/search call gives you a ticket_id. If description_html is large, fetch it in byte windows with field_ranges[description_html]=start-end instead of asking for a bigger list payload."
}
]
}
},
{
"match": {
"method": "post",
"path": "/api/v1/tickets"
},
"metadata": {
"displayName": "Create Ticket",
"summary": "Create a new ticket",
"description": "Creates a new ticket on a specified board with the desired status and priority. Requires the user to have ticket create permissions. Do not invoke this until you have gathered valid UUIDs for board_id, client_id, status_id, and priority_id from prior lookup calls. IMPORTANT: status_id must belong to the same board as board_id — use GET /api/v1/statuses?type=ticket&board_id=... to discover valid statuses for the chosen board.",
"rbacResource": "ticket",
"approvalRequired": true,
"requestBodySchema": {
"type": "object",
"properties": {
"title": { "type": "string", "description": "Ticket subject line." },
"board_id": { "type": "string", "format": "uuid", "description": "Ticket board identifier. Call GET /api/v1/boards to list available boards and pick a valid board_id. Always provide the UUID, never the board name, slug, or display label." },
"client_id": { "type": "string", "format": "uuid", "description": "Owning client identifier. Retrieve clients via GET /api/v1/clients (supporting filters such as name or status) to resolve the correct client_id, and provide that UUID in the payload (do not send the client name)." },
"status_id": { "type": "string", "format": "uuid", "description": "Initial ticket status identifier. Call GET /api/v1/statuses?type=ticket&board_id=... to list valid statuses for the chosen board. The status_id MUST belong to the same board as board_id. Do not send a status label like \"Open\" or a field named \"status\"." },
"priority_id": { "type": "string", "format": "uuid", "description": "Ticket priority identifier. Use GET /api/v1/priorities or sample existing tickets to obtain a valid priority_id. Always include the UUID (never send fields named \"priority\" or the textual priority name)." },
"contact_name_id": { "type": "string", "format": "uuid", "nullable": true, "description": "Primary contact for this ticket. Look up contacts through GET /api/v1/contacts (optionally filter by client_id) before supplying this reference." },
"location_id": { "type": "string", "format": "uuid", "nullable": true, "description": "Client location identifier. Call GET /api/v1/clients/{client_id}/locations to enumerate valid locations for the chosen client." },
"category_id": { "type": "string", "format": "uuid", "nullable": true, "description": "Ticket category identifier. Fetch categories via GET /api/v1/categories/ticket (filter by board_id if necessary) to resolve the correct category_id." },
"subcategory_id": { "type": "string", "format": "uuid", "nullable": true, "description": "Ticket subcategory identifier. After retrieving the category list or tree (GET /api/v1/categories/ticket or GET /api/v1/categories/ticket/tree/{board_id}), choose the desired subcategory_id." },
"assigned_to": { "type": "string", "format": "uuid", "nullable": true, "description": "Technician assignment. Use GET /api/v1/users (filtering by role, team, or name) to acquire the user_id of the assignee and submit that UUID." },
"url": { "type": "string", "format": "uri", "nullable": true },
"attributes": { "type": "object", "additionalProperties": true },
"tags": { "type": "array", "items": { "type": "string" }, "description": "Optional labels to associate with the ticket. Discover existing tag values via GET /api/v1/tags or GET /api/v1/tags/search before reusing them." }
},
"required": ["title", "board_id", "client_id", "status_id", "priority_id"]
},
"responseBodySchema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"ticket_id": { "type": "string", "format": "uuid" },
"ticket_number": { "type": "string" },
"title": { "type": "string" },
"status_id": { "type": "string", "format": "uuid" },
"priority_id": { "type": "string", "format": "uuid" },
"board_id": { "type": "string", "format": "uuid" },
"client_id": { "type": "string", "format": "uuid" },
"assigned_to": { "type": ["string", "null"], "format": "uuid" }
},
"required": [
"ticket_id",
"ticket_number",
"title",
"status_id",
"priority_id",
"board_id",
"client_id"
]
}
}
},
"examples": [
{
"name": "Create ticket example",
"request": {
"body": {
"title": "Printer offline at HQ",
"board_id": "11111111-1111-1111-1111-111111111111",
"client_id": "22222222-2222-2222-2222-222222222222",
"status_id": "33333333-3333-3333-3333-333333333333",
"priority_id": "44444444-4444-4444-4444-444444444444",
"contact_name_id": "55555555-5555-5555-5555-555555555555"
}
},
"notes": "Before calling this endpoint, resolve every referenced identifier by invoking the appropriate lookup APIs: GET /api/v1/boards to choose board_id, GET /api/v1/statuses?type=ticket&board_id=... to choose a status_id that belongs to that board, GET /api/v1/clients to choose client_id, GET /api/v1/contacts?client_id=... for contact_name_id, GET /api/v1/clients/{client_id}/locations for location_id, GET /api/v1/categories/ticket for category/subcategory options, GET /api/v1/users to select the assignee, and GET /api/v1/tags to reuse tag names. CRITICAL: status_id must belong to the same board as board_id — mismatched values will be rejected with a 400 error. Always send UUID fields exactly as documented — do not substitute human-readable names such as \"High\" or \"In Progress,\" and do not introduce extra fields that are not part of this schema."
}
]
}
}
,
{
"match": {
"method": "get",
"path": "/api/v1/tickets/{id}/comments"
},
"metadata": {
"displayName": "Get Ticket Comments",
"summary": "List comments for a ticket",
"description": "Returns the comments attached to a ticket. Use small limits and field-scoped byte ranges when comment bodies are large.",
"rbacResource": "ticket",
"approvalRequired": false,
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Ticket identifier.",
"schema": { "type": "string", "format": "uuid" }
},
{
"name": "limit",
"in": "query",
"required": false,
"description": "Maximum number of comments to return (1-200). Prefer small values when exploring ticket history.",
"schema": { "type": "integer", "minimum": 1, "maximum": 200 }
},
{
"name": "offset",
"in": "query",
"required": false,
"description": "Zero-based comment offset.",
"schema": { "type": "integer", "minimum": 0 }
},
{
"name": "order",
"in": "query",
"required": false,
"description": "Comment sort order.",
"schema": { "type": "string", "enum": ["asc", "desc"] }
},
{
"name": "field_ranges[comment_text]",
"in": "query",
"required": false,
"description": "Optional UTF-8 byte range for comment_text, formatted as start-end (for example 0-4095). Use meta.truncated_fields to continue fetching long comments.",
"schema": { "type": "string", "pattern": "^\\d+-\\d+$" }
},
{
"name": "field_ranges[comment_html]",
"in": "query",
"required": false,
"description": "Optional UTF-8 byte range for comment_html, formatted as start-end (for example 0-4095).",
"schema": { "type": "string", "pattern": "^\\d+-\\d+$" }
}
],
"examples": [
{
"name": "Read recent comments with ranged comment text",
"request": {
"params": {
"id": "11111111-1111-1111-1111-111111111111"
},
"query": {
"limit": 5,
"order": "desc",
"field_ranges[comment_text]": "0-4095"
}
},
"notes": "Prefer this endpoint over large ticket list payloads when the user asks for comment history or long comment bodies."
}
]
}
},
{
"match": {
"method": "post",
"path": "/api/v1/tickets/{id}/comments"
},
"metadata": {
"displayName": "Add Ticket Comment",
"summary": "Add a comment to a ticket",
"description": "Adds a client or internal comment to the specified ticket.",
"rbacResource": "ticket",
"approvalRequired": true,
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Ticket identifier.",
"schema": { "type": "string", "format": "uuid" }
}
],
"requestBodySchema": {
"type": "object",
"properties": {
"comment_text": {
"type": "string",
"description": "Comment body text."
},
"is_internal": {
"type": "boolean",
"description": "When true, records the comment as internal only.",
"default": false
},
"time_spent": {
"type": "number",
"description": "Optional time (in minutes) to log with the comment.",
"minimum": 0
}
},
"required": ["comment_text"]
}
}
},
{
"match": {
"method": "get",
"path": "/api/v1/tickets/{id}/time-entries"
},
"metadata": {
"displayName": "Get Ticket Time Entries",
"summary": "List time entries logged on a ticket",
"description": "Returns an authorization-aware summary of the time logged on a ticket. Each entry is evaluated through the authorization kernel against `time_entry:read`; the caller always sees their own entries, plus any other users' entries the kernel allows (e.g. team members managed by the caller, or entries permitted by an assigned authorization bundle). Entries the caller cannot see are still counted toward an anonymized aggregate (`othersHiddenCount` + `othersHiddenMinutes`) so that totals stay accurate without leaking individual entries.",
"rbacResource": "ticket",
"approvalRequired": false,
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Ticket identifier.",
"schema": { "type": "string", "format": "uuid" }
}
],
"responseSchema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"entries": {
"type": "array",
"description": "Visible time entries — always includes the caller's own entries; includes other users' entries that the authorization kernel allows for the caller (e.g. via team management or an authorization bundle).",
"items": {
"type": "object",
"properties": {
"entry_id": { "type": "string", "format": "uuid" },
"user_id": { "type": "string", "format": "uuid" },
"user_name": { "type": "string", "nullable": true },
"start_time": { "type": "string", "format": "date-time" },
"end_time": { "type": "string", "format": "date-time" },
"work_date": { "type": "string", "format": "date", "nullable": true },
"billable_duration": { "type": "integer", "description": "Duration in minutes." },
"notes": { "type": "string", "nullable": true },
"approval_status": {
"type": "string",
"enum": ["DRAFT", "SUBMITTED", "APPROVED", "CHANGES_REQUESTED"]
},
"service_id": { "type": "string", "format": "uuid", "nullable": true },
"service_name": { "type": "string", "nullable": true },
"is_own": { "type": "boolean" }
}
}
},
"ownTotalMinutes": {
"type": "integer",
"description": "Total billable minutes logged by the calling user."
},
"ownEntryCount": { "type": "integer" },
"othersTotalMinutes": {
"type": "integer",
"description": "Total billable minutes logged by other users (always populated, even when individual entries are hidden)."
},
"othersEntryCount": {
"type": "integer",
"description": "Total count of entries logged by other users (always populated)."
},
"othersVisibleMinutes": {
"type": "integer",
"description": "Subset of `othersTotalMinutes` for entries the caller is authorized to see in `entries`."
},
"othersVisibleCount": {
"type": "integer",
"description": "Subset of `othersEntryCount` for entries the caller is authorized to see in `entries`."
},
"othersHiddenMinutes": {
"type": "integer",
"description": "Remaining minutes from other users' entries that the caller is not authorized to see; surfaced as an anonymized aggregate."
},
"othersHiddenCount": {
"type": "integer",
"description": "Remaining count of other users' entries that the caller is not authorized to see; surfaced as an anonymized aggregate."
},
"totalMinutes": {
"type": "integer",
"description": "Sum of `ownTotalMinutes` and `othersTotalMinutes`."
}
},
"required": [
"entries",
"ownTotalMinutes",
"ownEntryCount",
"othersTotalMinutes",
"othersEntryCount",
"othersVisibleMinutes",
"othersVisibleCount",
"othersHiddenMinutes",
"othersHiddenCount",
"totalMinutes"
]
}
}
},
"examples": [
{
"name": "Read time logged on a ticket",
"request": {
"params": { "id": "11111111-1111-1111-1111-111111111111" }
},
"notes": "Use this endpoint to render time-tracking summaries on the ticket detail page or in mobile clients without paging through the global timesheet list."
}
]
}
}
]
}