# Ticket Time Entries, Notes & Status Changes — Working Payloads Verified request/response shapes for documenting work on tickets via `scripts/cw_api.py`. ## Response shape normalization (do this FIRST in every script) `cw_api.cw_request` returns `(data, http_status)` tuples, and list endpoints may wrap the payload one list deeper. Normalize before any `.get()` or loop, or scripts fail with `AttributeError: 'tuple' object has no attribute 'get'`: ```python def norm(resp): data = resp[0] if isinstance(resp, tuple) and len(resp) == 2 else resp if isinstance(data, list) and data and isinstance(data[0], list): data = data[0] return [x for x in (data if isinstance(data, list) else [data]) if isinstance(x, dict)] ``` ## Create a time entry on a ticket `POST /time/entries`. Convert local Pacific time to UTC by adding 7 hours (1:30–3:30 PM PDT → 20:30–22:30Z): ```json { "company": {"id": }, "member": {"id": 147}, "chargeToId": , "chargeToType": "ServiceTicket", "locationId": 11, "businessUnitId": 1, "workType": {"id": 3}, "workRole": {"id": 4}, "timeStart": "", "timeEnd": "", "actualHours": 2.0, "billableOption": "Billable", "notes": "", "addToDetailDescriptionFlag": true } ``` - `addToDetailDescriptionFlag` does NOT produce a ticket note on this instance — post the note separately if the text must appear on the ticket. - Verify with `GET /time/entries/{id}`: check `member.identifier`, `timeStart`/`timeEnd`, `actualHours`, and `notes` (top level; `_info.notes` only appears on the ticket timeEntries list view). - `billableOption`: "Billable" or "NoCharge". ## Add a ticket note `POST /service/tickets/{id}/notes`: ```json { "text": "", "detailDescriptionFlag": true, "internalAnalysisFlag": false, "resolutionFlag": false } ``` - Do NOT include `customerVisibleFlag` (or any field not on ServiceNote) — the API 400s the entire request: "Could not find member 'customerVisibleFlag' on object of type 'ServiceNote'". - Notes are attributed to the API member (TinMan), not an arbitrary author. - Verify with `GET /service/tickets/{id}/notes`. ## Change ticket status 1. Resolve the numeric status ID: `GET /service/boards/{boardId}/statuses`, match on `name` (VistaNet Help Desk board = 1; "In Progress" = id 1; names starting with `>` are closed-type statuses). 2. PATCH `/service/tickets/{id}` with a JSON Patch ARRAY: ```json [{"op": "replace", "path": "status/id", "value": 1}] ``` 3. Verify: `GET /service/tickets/{id}?fields=id,status`. ## Documentation audit of a scheduled day ```python # UTC bounds: Pacific day 00:00–24:00 PDT → add 7 hours params = {"conditions": f"member/id={memberId} AND dateStart >= [{startUtc}] AND dateStart <= [{endUtc}]", "pageSize": 100} entries = norm(cw_api.cw_request("GET", "schedule/entries", params=params)) # per entry: objectId = ticket id → GET /service/tickets/{id}/notes AND /service/tickets/{id}/timeEntries # count only notes where note["member"]["identifier"] matches the member — time entries do NOT count as notes # report per ticket: notes present / time-entry-only / nothing ```