syncro: expand ticket creation to full 19-field workflow

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 <noreply@anthropic.com>
This commit is contained in:
2026-04-23 11:47:12 -07:00
parent e7233d69a3
commit 0499f06ff8

View File

@@ -108,26 +108,177 @@ echo "Authenticated as: $ME"
|---|---|---|---|
| List tickets | GET | `/tickets?status=<status>&per_page=25` | — |
| Get ticket | GET | `/tickets/<id>` | — |
| 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/<id>` | `{"status": "In Progress", "priority": "..."}` |
| Delete ticket | DELETE | `/tickets/<id>` | — |
**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=<name>` 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=<name>&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: <name>
Subject: <subject>
Issue Type: <problem_type>
Priority: <priority>
Description: <description>
Due Date: <due_date>
Assigned To: <tech name>
Contact: <contact name>
Address: <address>
Do Not Email: <yes/no>
APPOINTMENT
-----------
Type: <type name>
Start: <start_at>
End: <end_at> (90 min)
Location: <location or blank>
ASSET: <asset name or none>
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": "<the full description>",
"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