Add /syncro command — Syncro PSA ticket management

Create, update, close, comment on, search, and bill tickets via Syncro
REST API. Includes customer search, invoice creation, line items, and
ticket timer management. API key from SOPS vault.

Verified: pulls real ticket data from computerguru.syncromsp.com.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 10:53:34 -07:00
parent 6bb00601b7
commit 046175af3a

145
.claude/commands/syncro.md Normal file
View File

@@ -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 <number> View ticket details + comments
/syncro create <customer> <subject> Create new ticket
/syncro update <number> <status> Update ticket status
/syncro close <number> Close/resolve a ticket
/syncro comment <number> <text> Add a comment to a ticket
/syncro bill <number> Create invoice from ticket time entries
/syncro search <query> Search tickets by subject/customer
/syncro customers <query> 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=<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=<status>&per_page=25` | — |
| Get ticket | GET | `/tickets/<id>` | — |
| Create ticket | POST | `/tickets` | `{"customer_id": N, "subject": "...", "problem_type": "...", "status": "New"}` |
| 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)
- `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/<id>/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=<search>&per_page=25` |
| Get customer | GET | `/customers/<id>` |
| Create customer | POST | `/customers` |
#### Invoices
| Operation | Method | Endpoint | Body |
|---|---|---|---|
| List invoices | GET | `/invoices?per_page=25` |
| Get invoice | GET | `/invoices/<id>` |
| 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=<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 <number>` 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/<customer>/session-logs/` documenting what was resolved. Pull the ticket subject, comments, and resolution into a structured log.