From 0499f06ff84870377af01df6acf651fe4e7ade16 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Thu, 23 Apr 2026 11:47:12 -0700 Subject: [PATCH] syncro: expand ticket creation to full 19-field workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the 3-call create pattern (ticket → Initial Issue comment → appointment), adds problem type and appointment type dropdowns with IDs, fixes priority format to number-prefixed strings ("2 Normal"), adds Howard to tech user ID table, and adds asset/contact lookup steps. Co-Authored-By: Claude Sonnet 4.6 --- .claude/commands/syncro.md | 179 ++++++++++++++++++++++++++++++++++--- 1 file changed, 165 insertions(+), 14 deletions(-) diff --git a/.claude/commands/syncro.md b/.claude/commands/syncro.md index 5fe2e81..bb713d7 100644 --- a/.claude/commands/syncro.md +++ b/.claude/commands/syncro.md @@ -108,26 +108,177 @@ echo "Authenticated as: $ME" |---|---|---|---| | List tickets | GET | `/tickets?status=&per_page=25` | — | | Get ticket | GET | `/tickets/` | — | -| Create ticket | POST | `/tickets` | `{"customer_id": N, "subject": "...", "problem_type": "...", "status": "New"}` | +| Create ticket | POST | `/tickets` | see full create workflow below | | Update ticket | PUT | `/tickets/` | `{"status": "In Progress", "priority": "..."}` | | Delete ticket | DELETE | `/tickets/` | — | **Ticket statuses:** `New`, `In Progress`, `Waiting on Customer`, `Waiting on Vendor`, `Scheduled`, `Resolved`, `Invoiced`, `Closed` -**Ticket fields (create/update):** -- `customer_id` (required for create) -- `subject` (required for create) -- `problem_type` (string, free-form) -- `status` (string, one of the statuses above) -- `priority` (string) — set this; leave blank only if user says not to -- `due_date` (ISO date) -- `user_id` (assign to tech) — set this; Mike = 1735, Winter = 1737, Rob = 1760 -- `contact_id` (customer contact) -- `ticket_type_id` (ticket category) +**Priority format** (number-prefixed string): `"1 High"`, `"2 Normal"`, `"3 Low"`, `"4 Urgent"` +Default: `"2 Normal"`. Use `"4 Urgent"` for emergency/after-hours. -**Always set `user_id` and `priority` on create** unless the user says otherwise. Ask if unknown. -- Assignee = whoever worked the ticket (Mike = 1735, Winter = 1737, Rob = 1760) -- Priority = `Normal` by default; `Urgent` for emergency/after-hours tickets +**Problem types (Issue Type dropdown — use closest match, else "Not determined"):** +`API`, `Email`, `Emergency Service`, `File Services / Permissions`, `Hardware`, `Maintenance`, +`New User / M365 Account Creation`, `New User / Workstation Deployment`, `Not determined`, +`Onsite`, `Other`, `Phone/VOIP`, `Remote`, `Security`, `Server Migration`, `Service Request`, +`Software`, `Website` + +**Appointment types:** + +| Name | ID | location_type | +|---|---|---| +| In Shop | 4321 | shop | +| Onsite | 4322 | customer | +| Phone Call | 4323 | pre_defined | +| Reminder | 193053 | manual_entry | +| Remote | 59289 | pre_defined | + +**Tech user IDs:** Mike = 1735, Howard = 1750, Winter = 1737, Rob = 1760 + +--- + +### Ticket creation workflow (full — 3 API calls) + +Ticket creation in Syncro maps to three separate API calls. Gather all inputs first, show a full preview, wait for confirmation, then execute in order. + +#### Step 1 — Gather inputs + +Collect in one pass (do not ask field by field): + +| # | Field | Notes | +|---|---|---| +| 1 | **Subject** | Brief title: reason for the ticket | +| 2 | **Issue Type** (`problem_type`) | From dropdown above; "Not determined" if unclear | +| 3 | **Priority** | "2 Normal" default; "4 Urgent" for emergencies | +| 4 | **Description** | Expanded detail — becomes the "Initial Issue" comment body | +| 5 | **Do Not Email** | Suppress customer notification on ticket create? (yes for internal/reminder tickets) | +| 6 | **Due Date** | ISO date | +| 7 | **Assigned Tech** | Who owns the ticket | +| 8 | **Contact** | Look up from `GET /customers/{id}` → `.contacts[]`; show list, ask user to pick | +| 9 | **Address/Site** | `address_id` — also comes from customer contacts with address data | +| 10 | **Appointment Type** | From table above; omit section if no appointment needed | +| 11 | **Location** | Free text; usually blank unless onsite at non-primary address | +| 12 | **Start Time** | ISO8601 datetime; omit if no scheduled appointment | +| 13 | **End Time** | Default: start + 90 minutes | +| 14 | **Appointment Owner** | Usually same as assigned tech; noted for calendar attribution (not a separate API field — inherits from ticket `user_id`) | +| 15 | **Do Not Invite** | If not onsite, suppress calendar invite — note: not directly controllable via API; inform user if they need this set manually | +| 16 | **Asset** | Search `GET /customer_assets?customer_id=N&query=` if a specific device is involved | + +#### Step 2 — Look up customer data + +Before showing the preview, fetch what you need: + +```bash +# Get contacts and addresses +curl -s "${BASE}/customers/${CUST_ID}?api_key=${API_KEY}" | jq '{contacts: [.customer.contacts[] | {id, name, address1, email}]}' + +# Search assets +curl -s "${BASE}/customer_assets?customer_id=${CUST_ID}&query=&api_key=${API_KEY}" | jq '[.assets[] | {id, name, asset_type}]' +``` + +#### Step 3 — Show preview and confirm + +Display the full ticket before posting. Include all populated fields. Wait for explicit confirmation. + +``` +TICKET PREVIEW +-------------- +Customer: +Subject: +Issue Type: +Priority: +Description: +Due Date: +Assigned To: +Contact: +Address:
+Do Not Email: + +APPOINTMENT +----------- +Type: +Start: +End: (90 min) +Location: + +ASSET: + +Confirm? (yes/no) +``` + +#### Step 4 — Execute (after confirmation) + +**Call 1 — Create ticket:** + +```bash +curl -s -X POST "${BASE}/tickets?api_key=${API_KEY}" \ + -H "Content-Type: application/json" \ + -d @/tmp/ticket_payload.json +# Parse: TICKET_ID=$(... | jq -r '.ticket.id') +# Parse: CUST_ID=$(... | jq -r '.ticket.customer_id') +``` + +Payload fields (omit null/blank): +```json +{ + "customer_id": N, + "subject": "...", + "problem_type": "...", + "status": "New", + "priority": "2 Normal", + "user_id": N, + "due_date": "YYYY-MM-DD", + "contact_id": N, + "address_id": N, + "start_at": "ISO8601", + "end_at": "ISO8601", + "asset_ids": [N] +} +``` + +**Call 2 — Post initial description as "Initial Issue" comment:** + +```bash +curl -s -X POST "${BASE}/tickets/${TICKET_ID}/comment?api_key=${API_KEY}" \ + -H "Content-Type: application/json" \ + -d @/tmp/comment_payload.json +# Parse: .comment.id (NOT .id — see Hard Rules) +``` + +Payload: +```json +{ + "subject": "Initial Issue", + "body": "", + "hidden": false, + "do_not_email": true +} +``` +Set `do_not_email: true` if "Do Not Email" was checked; `false` otherwise. + +**Call 3 — Create appointment (only if start_at provided):** + +```bash +curl -s -X POST "${BASE}/appointments?api_key=${API_KEY}" \ + -H "Content-Type: application/json" \ + -d @/tmp/appt_payload.json +``` + +Payload: +```json +{ + "ticket_id": N, + "customer_id": N, + "appointment_type_id": N, + "start_at": "ISO8601", + "end_at": "ISO8601", + "location": "" +} +``` + +Note: "Do Not Invite" (suppress calendar invite email) is not API-controllable. Tell the user to toggle it in the Syncro GUI if needed. + +**Always use temp files for payloads** — never inline JSON in curl -d with ticket data (special characters, newlines in description will break the shell). #### Comments