{"openapi":"3.1.0","info":{"title":"HerWay TMS SaaS API","version":"1.0.0","description":"Multi-tenant REST API for transport companies and customer integrations. Authenticate with a tenant API key. Customer-scoped keys only see that customer's consignments.","contact":{"name":"HerWay TMS"}},"servers":[{"url":"/api/v1","description":"Versioned REST API"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Tenant or customer API key (hw_live_…)."},"BearerAuth":{"type":"http","scheme":"bearer","description":"Same key as Bearer token."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true}}}}},"Branding":{"type":"object","properties":{"displayName":{"type":["string","null"]},"primaryColour":{"type":["string","null"]},"logoUrl":{"type":["string","null"]}}},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string","enum":["active","inactive"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"creditHold":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}},"Consignment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"jobNumber":{"type":"string"},"status":{"type":"string"},"senderReference":{"type":["string","null"]},"sendingDepot":{"type":["string","null"]},"customerName":{"type":["string","null"]},"customerId":{"type":["string","null"],"format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"paths":{"/health":{"get":{"summary":"API health","security":[],"responses":{"200":{"description":"API is reachable"}}}},"/me":{"get":{"summary":"Authenticate and return tenant + white-label branding","responses":{"200":{"description":"Current API key context"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers":{"get":{"summary":"List customers","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Paginated customers"},"403":{"description":"Missing customers:read scope"}}}},"/customers/{id}":{"get":{"summary":"Get customer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"404":{"description":"Not found"}}}},"/consignments":{"get":{"summary":"List consignments","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}},{"name":"page","in":"query","schema":{"type":"integer"}},{"name":"pageSize","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Paginated consignments"}}},"post":{"summary":"Create consignment (draft or booked)","description":"Requires consignments:write. Uses api_create_job RPC.","responses":{"201":{"description":"Created"},"400":{"description":"Validation error"},"403":{"description":"Missing scope"},"409":{"description":"Credit hold / conflict"}}}},"/depots":{"get":{"summary":"List active depots","responses":{"200":{"description":"Depot master rows"}}}},"/consignments/{id}":{"get":{"summary":"Get consignment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Consignment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Consignment"}}}}}},"patch":{"summary":"Update consignment fields","responses":{"200":{"description":"Updated"},"409":{"description":"Version conflict"}}},"delete":{"summary":"Cancel consignment (soft)","responses":{"200":{"description":"Cancelled"}}}},"/consignments/{id}/status":{"get":{"summary":"Get consignment status","responses":{"200":{"description":"Status"}}},"post":{"summary":"Update consignment status","responses":{"200":{"description":"Transitioned"}}}},"/consignments/{id}/items":{"post":{"summary":"Add freight items","responses":{"201":{"description":"Created"}}}},"/consignments/{id}/accessorials":{"post":{"summary":"Add accessorial charge","responses":{"201":{"description":"Created"}}}},"/consignments/{id}/fields":{"patch":{"summary":"Single field update","responses":{"200":{"description":"Updated"}}}},"/consignments/{id}/pod":{"post":{"summary":"Create POD record","responses":{"201":{"description":"Created"}}}},"/consignments/{id}/documents":{"get":{"summary":"Document / print paths","responses":{"200":{"description":"Document links"}}}},"/price-estimate":{"post":{"summary":"Indicative price estimate from rate cards","description":"Estimates only — not a binding quote. Response amounts are in AUD dollars (amountAud).","responses":{"200":{"description":"Indicative estimates (amountAud)"}}}},"/suburbs":{"post":{"summary":"Validate Australian suburb/postcode","responses":{"200":{"description":"Validated"}}}},"/openapi.json":{"get":{"summary":"OpenAPI document","security":[],"responses":{"200":{"description":"OpenAPI 3.1 JSON"}}}}}}