From aa3b524df102b67fd03f4fdc1b0594c562d32161 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Fri, 22 May 2026 12:06:09 -0700 Subject: [PATCH] sync: auto-sync from DESKTOP-0O8A1RL at 2026-05-22 12:06:06 Author: Mike Swanson Machine: DESKTOP-0O8A1RL Timestamp: 2026-05-22 12:06:06 --- .claude/commands/syncro.md | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/.claude/commands/syncro.md b/.claude/commands/syncro.md index 598c640..1dcba7f 100644 --- a/.claude/commands/syncro.md +++ b/.claude/commands/syncro.md @@ -441,9 +441,15 @@ Every endpoint's response shape, verified against the live API. Parse exactly as | Create appointment | POST `/appointments` | `{"appointment": {...}}` | `.appointment.id` | | Update appointment | PUT `/appointments/{id}` | `{"appointment": {...}}` | `.appointment.start_at` | | Delete appointment | DELETE `/appointments/{id}` | `{"message": "deleted"}` | — | +| Create estimate | POST `/estimates` | `{"estimate": {...}}` | `.estimate.id`, `.estimate.number` | +| Add estimate line item | POST `/estimates/{id}/line_items` | `{"estimate": {...}, "line_item": {"id": N, ...}}` | `.line_item.id` | +| Get estimate | GET `/estimates/{id}` | `{"estimate": {"line_items": [...]}}` | `.estimate.line_items[].price` | +| Delete estimate | DELETE `/estimates/{id}` | `{"message": "N: We deleted # NNNN. "}` | — | **Invoice GET line_items field names differ from ticket line_items:** `item` = product name, `name` = description, `price` = unit rate. Do not use `price_retail` when reading invoice line items. +**Estimate line items use the same field naming as invoice line_items:** `item` = product name, `name` = description, `price` = unit rate (not `price_retail`). The add-line-item endpoint is `POST /estimates/{id}/line_items` — NOT `add_line_item` (that path 404s). Response includes both the updated estimate and the created line_item as sibling keys. + **`start_at` in ticket POST is unreliable** — always create appointments via separate POST `/appointments`. Do not rely on `start_at`/`end_at` on the ticket object itself. --- @@ -576,6 +582,60 @@ curl -s -X DELETE "${BASE}/invoices/${INV_ID}?api_key=${API_KEY}" POST `/invoices` pulls all current line items from the ticket into the invoice automatically. The POST response includes `.invoice.id` and `.invoice.total` — if either is null, GET `/invoices?customer_id=${CUST_ID}&per_page=5` and find the invoice by `ticket_id` match before taking any other action. +#### Estimates + +Estimates (quotes) are standalone or ticket-linked. Verified 2026-05-22 against ACG internal account. + +**Required fields for POST /estimates:** `customer_id`, `date` (ISO date string `"YYYY-MM-DD"`) +**Optional:** `name` (estimate title), `ticket_id` (link to ticket), `location_id` +**Statuses:** `Fresh` (default), `Approved`, `Declined` + +```bash +# Create estimate +EST_RESP=$(curl -s -X POST "${BASE}/estimates?api_key=${API_KEY}" \ + -H "Content-Type: application/json" \ + --data-binary @- <", + "date": "$(date +%Y-%m-%d)" +} +JSON +) +ESTIMATE_ID=$(echo "$EST_RESP" | jq -r '.estimate.id') +ESTIMATE_NUM=$(echo "$EST_RESP" | jq -r '.estimate.number') + +# Add line item — endpoint is /line_items NOT /add_line_item (that 404s) +# Response: {"estimate": {...}, "line_item": {"id": N, "item": name, "price": rate, ...}} +LI_RESP=$(curl -s -X POST "${BASE}/estimates/${ESTIMATE_ID}/line_items?api_key=${API_KEY}" \ + -H "Content-Type: application/json" \ + --data-binary @- <", + "description": "", + "quantity": ${QTY}, + "price_retail": ${RATE}, + "taxable": false +} +JSON +) +LI_ID=$(echo "$LI_RESP" | jq -r '.line_item.id') + +# GET estimate (verify) +curl -s "${BASE}/estimates/${ESTIMATE_ID}?api_key=${API_KEY}" | \ + jq '{id: .estimate.id, number: .estimate.number, total: .estimate.total, + lines: [.estimate.line_items[]? | {id, item, name, quantity, price}]}' + +# DELETE estimate +curl -s -X DELETE "${BASE}/estimates/${ESTIMATE_ID}?api_key=${API_KEY}" +# Response: {"message": "N: We deleted # NNNN. "} +``` + +**Line item field naming in estimate responses:** `item` = product name, `name` = description, `price` = unit rate. This matches invoice line_items, NOT ticket `add_line_item` (which uses `price_retail`). + +**GET /estimates line_items vs POST response:** GET returns `line_items` as an array on `.estimate.line_items[]`. POST `/line_items` returns the line item under `.line_item` (singular, not nested under estimate). + ### Display formatting When showing ticket lists, format as: