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
This commit is contained in:
@@ -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 @- <<JSON
|
||||
{
|
||||
"customer_id": ${CUST_ID},
|
||||
"name": "Estimate for <subject>",
|
||||
"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 @- <<JSON
|
||||
{
|
||||
"product_id": ${PRODUCT_ID},
|
||||
"name": "<product name>",
|
||||
"description": "<one-line 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:
|
||||
|
||||
Reference in New Issue
Block a user