diff --git a/.claude/commands/syncro.md b/.claude/commands/syncro.md new file mode 100644 index 0000000..08b6838 --- /dev/null +++ b/.claude/commands/syncro.md @@ -0,0 +1,145 @@ +# /syncro — Syncro PSA ticket management + +Create, update, close, comment on, and bill tickets in Syncro PSA. + +## Usage + +``` +/syncro Show open tickets summary +/syncro ticket View ticket details + comments +/syncro create Create new ticket +/syncro update Update ticket status +/syncro close Close/resolve a ticket +/syncro comment Add a comment to a ticket +/syncro bill Create invoice from ticket time entries +/syncro search Search tickets by subject/customer +/syncro customers Search customers +``` + +## API Configuration + +**Base URL:** `https://computerguru.syncromsp.com/api/v1` +**API Key:** SOPS vault `msp-tools/syncro.sops.yaml` → `credentials.credential` +**Rate limit:** 180 requests/minute per IP +**Docs:** https://api-docs.syncromsp.com/ + +## Implementation + +When invoked, use the Syncro REST API via `curl`. All requests include `?api_key=` as query parameter (NOT in header — Syncro uses query param auth). + +### Get API key + +```bash +API_KEY=$(bash D:/vault/scripts/vault.sh get-field msp-tools/syncro.sops.yaml credentials.credential) +BASE="https://computerguru.syncromsp.com/api/v1" +``` + +If `vault.sh get-field` fails (yq not installed), fall back to: +```bash +API_KEY=$(sops -d D:/vault/msp-tools/syncro.sops.yaml | python -c "import sys,yaml; print(yaml.safe_load(sys.stdin)['credentials']['credential'])") +``` + +### Endpoints reference + +#### Tickets + +| Operation | Method | Endpoint | Body | +|---|---|---|---| +| List tickets | GET | `/tickets?status=&per_page=25` | — | +| Get ticket | GET | `/tickets/` | — | +| Create ticket | POST | `/tickets` | `{"customer_id": N, "subject": "...", "problem_type": "...", "status": "New"}` | +| 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) +- `due_date` (ISO date) +- `user_id` (assign to tech) +- `contact_id` (customer contact) +- `ticket_type_id` (ticket category) + +#### Comments + +| Operation | Method | Endpoint | Body | +|---|---|---|---| +| Add comment | POST | `/tickets//comment` | `{"subject": "Update", "body": "...", "hidden": false, "do_not_email": false}` | + +**Comment fields:** +- `subject` — comment header (e.g., "Update", "Resolution", "Internal Note") +- `body` — comment text (HTML supported) +- `hidden` — if true, internal-only (customer can't see) +- `do_not_email` — if true, don't email customer about this comment + +#### Customers + +| Operation | Method | Endpoint | +|---|---|---| +| List/search | GET | `/customers?query=&per_page=25` | +| Get customer | GET | `/customers/` | +| Create customer | POST | `/customers` | + +#### Invoices + +| Operation | Method | Endpoint | Body | +|---|---|---|---| +| List invoices | GET | `/invoices?per_page=25` | +| Get invoice | GET | `/invoices/` | +| Create from ticket | POST | `/invoices` | `{"ticket_id": N, "customer_id": N}` | + +#### Line Items + +| Operation | Method | Endpoint | Body | +|---|---|---|---| +| Add line item | POST | `/line_items` | `{"invoice_id": N, "item": "...", "quantity": 1, "price": 125.00}` | + +#### Ticket Timers + +| Operation | Method | Endpoint | +|---|---|---| +| List | GET | `/ticket_timers?ticket_id=` | +| Create | POST | `/ticket_timers` | + +### Display formatting + +When showing ticket lists, format as: + +``` +#32164 New Jerry Burger Own cloud thing again +#32163 New LeeAnn Parkinson Remote - Jim cant access his email +#32162 Invoiced Len's Auto Brokerage Server upgrade +``` + +When showing ticket detail, include: +- Ticket number, subject, status, priority +- Customer name + contact +- Created date, due date, last updated +- Assigned tech +- Comments (most recent first, truncated to last 5) +- Time entries if any +- Billing status + +### Billing workflow + +When `/syncro bill ` is called: +1. Get ticket details + time entries +2. Show summary: total billable time, line items that would be created +3. Ask for confirmation: "Create invoice for $X? [yes/no]" +4. On yes: POST `/invoices` with ticket_id, then add line items +5. Update ticket status to "Invoiced" + +### Error handling + +- 401: API key invalid or expired +- 404: ticket/customer/invoice not found +- 422: validation error (show the error message from response body) +- 429: rate limited (wait 60s and retry) + +### Integration with session logs + +When closing a ticket (`/syncro close`), offer to create a session log entry in `clients//session-logs/` documenting what was resolved. Pull the ticket subject, comments, and resolution into a structured log.