Requests & Responses
Typed ids, decimal-string money, UTC instants, wall-clock appointment times, and the object/list envelopes.
Objects
Every resource has id and object. Ids are typed and opaque: a prefix names the resource type, so a vehicle id in a customer slot is rejected as invalid_id before any lookup.
| Prefix | Resource | Prefix | Resource |
|---|---|---|---|
shop_ |
shop | inv_ |
invoice / credit note |
cus_ |
customer | pay_ |
payment |
veh_ |
vehicle | item_ |
inventory item |
appt_ |
appointment | cj_ |
canned job |
ro_ |
repair order | tech_ |
technician |
job_ |
job (line item) | task_ |
task |
part_ / lab_ |
part / labor entry | whe_ / evt_ |
webhook endpoint / event |
Ids are stable for the life of the record. Never parse them.
Money
Amounts are decimal strings — "129.99", never floats — with the currency named alongside ("currency": "CAD"). The shop's currency comes from GET /v1/shop; every monetary object repeats it. Do the maths in decimal types.
Dates and times
- Response instants (
created_at,updated_at,posted_at, …) are RFC 3339 in UTC with aZsuffix:2026-09-01T14:03:27Z. Instant request filters/fields require eitherZor an explicit numeric offset; naive datetimes are rejected. - Appointments are wall-clock:
date(2026-09-14) and exact minutetime(09:30, no seconds or zone) in the object's IANAtimezone. A DST-gap time is rejected. A repeated fall-back time requiresutc_offsetsuch as-04:00or-05:00. Responses include the resolved UTCstarts_at. - Dates on financial documents (
invoice_date,payment_business_date) are the shop's business dates.
Requests
- JSON bodies with
Content-Type: application/json; unknown fields are rejected (invalid_requestwithparam). - PATCH operations are presence-aware: omitted means unchanged. Explicit
nullclears only documented nullable fields; it is rejected for required/non-clearable fields.false,0,[], and""are values, not omission. An empty PATCH is400 no_changes. - Object references in bodies use public ids (
"customer_id": "cus_…"). - Body size is capped at 1 MB.
Response envelopes
A single resource is returned bare. Lists use:
{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "eyJ…" }
Errors use one shape everywhere — see Errors.
Headers on every response
| Header | Meaning |
|---|---|
X-Request-Id |
req_… — quote it when contacting support; the shop can trace it in its activity log |
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset |
Current budget — see Rate limits |
Cache-Control: no-store |
Responses are never cacheable |
Sensitive fields
Fields the integration's scopes do not cover are redacted, not omitted: a customer read without customers.read is not possible at all; RO/job/part/labor/canned-job sale pricing needs pricing.read; and acquisition cost needs inventory.cost.read. A denied field is null and its JSON pointer appears in redacted_fields. Treat that null as "not visible to you", not zero.
Consistency rules you can rely on
- Additive changes only within a version: new fields and enum values can appear; nothing is renamed or removed (see Versioning).
- Workflow state enums include
unknown; raw future internal values are never exposed. Treatunknownas an explicit prompt to refresh/report, not as another known state. - Every write returns the full resulting object, so you never need a follow-up GET to learn what you changed.