Files
claudetools/.claude/commands/syncro.md
Mike Swanson ac4ceb65c0 Fix /syncro: time is added via comment fields, not timer_entry
Discovered from GUI page source: comment[product_id] + comment[minutes_spent]
+ comment[bill_time_now] are fields on POST /tickets/{id}/comment. This is
how the GUI adds time — as part of the comment, not via separate timer_entry.
Updated billing workflow + added --time/--labor flags to comment command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:17:40 -07:00

6.9 KiB

/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.yamlcredentials.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

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:

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 (with optional time entry)

Operation Method Endpoint Body
Add comment POST /tickets/<id>/comment {"subject": "Update", "body": "...", "hidden": false, "do_not_email": false}
Add comment + time POST /tickets/<id>/comment Same as above, PLUS: "product_id": N, "minutes_spent": 60, "bill_time_now": 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
  • product_id — labor product ID (see labor products table below). Adds billable time to the ticket.
  • minutes_spent — integer, minutes of work (60 = 1hr minimum in most cases)
  • bill_time_now — if true, immediately creates a charge (equivalent to "Charge now" checkbox in GUI)

This is the primary way to log time. Comment + time in one call mirrors the GUI workflow exactly. Timer entries (/tickets/{id}/timer_entry) exist but are rarely used.

Customers

Operation Method Endpoint
List/search GET /customers?query=<search>&per_page=25
Get customer GET /customers/<id>
Create customer POST /customers

Timer Entries (add time to ticket)

Operation Method Endpoint Body
Add time POST /tickets/<id>/timer_entry {"start_at": "ISO8601", "end_at": "ISO8601", "notes": "...", "billable": true, "product_id": N}
List timers GET /ticket_timers?ticket_id=<id>

IMPORTANT: product_id must be a labor product, not an invoice product. Common labor products:

  • 1190473 — Labor - Remote Business (standard remote work)
  • 26118 — Labor - Onsite Business
  • 26184 — Labor - Emergency or After Hours Business
  • 9269129 — Labor - Prepaid Project Labor
  • 9269124 — Labor - Internal Labor
  • 26117 — Fee - Travel Time
  • 68055 — Labor - Website Labor

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, "category": "Standard"}
Delete invoice DELETE /invoices/<id>

"Make Invoice" flow: Timer entries on the ticket become invoice line items when you POST /invoices with the ticket_id. This is the equivalent of clicking "Make Invoice" in the GUI.

Invoice Line Items

Operation Method Endpoint Body
Add line item POST /invoices/<id>/line_items {"item": "...", "quantity": 1, "price": 125.00, "product_id": N}

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
  2. Ask: "How many minutes + labor type?" (default: 60 min, Labor - Remote Business)
  3. Add comment with time: POST /tickets/{id}/comment with product_id, minutes_spent, bill_time_now: false, and work notes as body
  4. Then create invoice: POST /invoices with {"ticket_id": N, "customer_id": N, "category": "Standard"}
  5. Update ticket status to "Invoiced"

The flow mirrors the GUI: add comment with time attached → Make Invoice.

When /syncro comment <number> <text> --time 60 --labor remote is called:

  • Post the comment with time in one API call
  • --labor maps to product IDs: remote → 1190473, onsite → 26118, emergency → 26184, project → 9269129, internal → 9269124, travel → 26117, website → 68055

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.