{"openapi":"3.1.0","info":{"title":"RepairHQ Public API","version":"1.0.0","description":"Read-only, business- and location-scoped API. A resource requires its matching :read scope. Tokens are managed by a store owner or admin in API & integrations. Documentation: https://www.repairhq.io/developers"},"servers":[{"url":"https://www.repairhq.io"}],"security":[{"scopedToken":[]}],"tags":[{"name":"Records","description":"Read scoped shop records."}],"paths":{"/api/public/v1":{"get":{"operationId":"listRecords","tags":["Records"],"summary":"List records in the token’s location","description":"Returns up to 50 records, ordered by UUID. This is not a chronological change feed. At most 60 requests per token per fixed minute window. No writes, record-ID lookup, date filter, page-size parameter, or cross-location selector is supported.","parameters":[{"name":"resource","in":"query","required":false,"description":"The token must include the corresponding resource:read scope.","schema":{"type":"string","enum":["repairs","inventory","customers","events"],"default":"repairs"}},{"name":"after","in":"query","required":false,"description":"Opaque cursor from the previous response’s next value, using the same resource and token.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"A page of scoped records.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/RepairPage"},{"$ref":"#/components/schemas/InventoryItemPage"},{"$ref":"#/components/schemas/CustomerPage"},{"$ref":"#/components/schemas/EventPage"}],"discriminator":{"propertyName":"resource","mapping":{"repairs":"#/components/schemas/RepairPage","inventory":"#/components/schemas/InventoryItemPage","customers":"#/components/schemas/CustomerPage","events":"#/components/schemas/EventPage"}}},"examples":{"repairs":{"summary":"Repairs — synthetic example","value":{"resource":"repairs","data":[{"id":"11111111-1111-4111-8111-111111111111","number":"1042","device":"Sample laptop","status":"In repair","priority":"Normal","due_at":"2026-11-02","updated_at":"2026-10-27T14:30:00.000Z"}],"next":null}},"inventory":{"summary":"Inventory — synthetic example","value":{"resource":"inventory","data":[{"id":"22222222-2222-4222-8222-222222222222","sku":"SAMPLE-CABLE","name":"Sample charging cable","category":"Accessory","barcode":"","quantity":12,"available_quantity":10,"price_cents":2499,"shelf_bin":"A-03"}],"next":null}},"customers":{"summary":"Customers — synthetic example","value":{"resource":"customers","data":[{"id":"33333333-3333-4333-8333-333333333333","name":"Sample Customer","email":"customer@example.invalid","phone":"","address":"","city":"","state":"","postal_code":"","service_email":true,"service_sms":false}],"next":null}},"events":{"summary":"Events — synthetic example","value":{"resource":"events","data":[{"id":"44444444-4444-4444-8444-444444444444","kind":"ticket","record_id":"11111111-1111-4111-8111-111111111111","action":"Repair status changed","created_at":"2026-10-27T14:30:00.000Z"}],"next":null}}}}}},"400":{"description":"Invalid resource or cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"after must be a valid UUID cursor."}}}},"401":{"description":"Missing, malformed, expired, or revoked bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"A valid scoped API token is required"}}}},"403":{"description":"The token does not permit this resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"This token does not allow this resource"}}}},"405":{"description":"Method not allowed. Only GET, HEAD, and OPTIONS are supported; this response may have no JSON body."},"429":{"description":"Per-token request limit exceeded. Retry after 60 seconds with jitter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit reached. Retry after one minute."}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","const":60}}}},"500":{"description":"Unexpected server error. Retry with bounded exponential backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"The request could not be completed."}}}}}}}},"webhooks":{"shopEvent":{"post":{"summary":"Signed event sent to your configured endpoint","description":"Configure one event type per endpoint in API & integrations. Verify HMAC over delivery ID + dot + Unix timestamp + dot + raw request bytes. See /developers#webhooks for delivery scheduling, deduplication, and retries.","security":[],"parameters":[{"name":"X-RHQ-Delivery-ID","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"X-RHQ-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"}},{"name":"X-RHQ-Signature","in":"header","required":true,"schema":{"type":"string","pattern":"^v1=[a-f0-9]{64}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"44444444-4444-4444-8444-444444444444","type":"repair.updated","createdAt":"2026-10-27T14:30:00.000Z","businessId":"55555555-5555-4555-8555-555555555555","locationId":"66666666-6666-4666-8666-666666666666","recordId":"11111111-1111-4111-8111-111111111111","action":"Repair status changed"}}}},"responses":{"2XX":{"description":"Event accepted after durable storage or previously processed delivery acknowledged."},"default":{"description":"Non-2xx or timeout counts as a failed attempt; see retry policy."}}}}},"components":{"securitySchemes":{"scopedToken":{"type":"http","scheme":"bearer","bearerFormat":"rhq_ + 64 lowercase hexadecimal characters","description":"Send Authorization: Bearer <token>. Only the selected resources and token’s business/location are accessible. Keep the token on your server."}},"schemas":{"Repair":{"type":"object","required":["id","number","device","status","priority","due_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Repair ticket ID."},"number":{"oneOf":[{"type":"string","pattern":"^[0-9]+$"},{"type":"integer"}],"description":"Ticket number. Accept a decimal string or integer; preserve large numbers as strings."},"device":{"type":"string","description":"Device description."},"status":{"type":"string","description":"Current workflow status."},"priority":{"type":"string","enum":["Low","Normal","High","Urgent"],"description":"Ticket priority."},"due_at":{"type":["string","null"],"format":"date","description":"Due date, or null when unset."},"updated_at":{"type":"string","format":"date-time","description":"Last recorded update."}}},"RepairPage":{"type":"object","required":["resource","data","next"],"properties":{"resource":{"type":"string","const":"repairs"},"data":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/Repair"}},"next":{"type":["string","null"],"format":"uuid","description":"Pass this cursor as after. Null means this traversal has ended."}}},"InventoryItem":{"type":"object","required":["id","sku","name","category","barcode","quantity","available_quantity","price_cents","shelf_bin"],"properties":{"id":{"type":"string","format":"uuid","description":"Inventory item ID."},"sku":{"type":"string","description":"Location SKU."},"name":{"type":"string","description":"Product name."},"category":{"type":"string","description":"Product category; may be empty."},"barcode":{"type":"string","description":"Barcode; may be empty."},"quantity":{"type":"integer","description":"On-hand quantity."},"available_quantity":{"type":"integer","description":"On hand minus active, unexpired part reservations, with a minimum of zero."},"price_cents":{"type":"integer","description":"Selling price in cents; 2499 means 24.99 in the shop currency."},"shelf_bin":{"type":"string","description":"Shelf or bin; may be empty."}}},"InventoryItemPage":{"type":"object","required":["resource","data","next"],"properties":{"resource":{"type":"string","const":"inventory"},"data":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/InventoryItem"}},"next":{"type":["string","null"],"format":"uuid","description":"Pass this cursor as after. Null means this traversal has ended."}}},"Customer":{"type":"object","required":["id","name","email","phone","address","city","state","postal_code","service_email","service_sms"],"properties":{"id":{"type":"string","format":"uuid","description":"Customer ID."},"name":{"type":"string","description":"Customer name."},"email":{"type":"string","description":"Email address; may be empty."},"phone":{"type":"string","description":"Phone number; may be empty."},"address":{"type":"string","description":"Address text; legacy records may contain a full formatted address."},"city":{"type":"string","description":"City; may be empty."},"state":{"type":"string","description":"State or region; may be empty."},"postal_code":{"type":"string","description":"ZIP or postal code; may be empty."},"service_email":{"type":"boolean","description":"Recorded service email preference; not blanket marketing consent."},"service_sms":{"type":"boolean","description":"Recorded service SMS preference; not blanket marketing consent."}}},"CustomerPage":{"type":"object","required":["resource","data","next"],"properties":{"resource":{"type":"string","const":"customers"},"data":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/Customer"}},"next":{"type":["string","null"],"format":"uuid","description":"Pass this cursor as after. Null means this traversal has ended."}}},"Event":{"type":"object","required":["id","kind","record_id","action","created_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Event ID."},"kind":{"type":"string","enum":["job","ticket","invoice","inventory"],"description":"Record category; repairs use ticket."},"record_id":{"type":"string","format":"uuid","description":"Related record ID."},"action":{"type":"string","description":"Human-readable activity description; not a stable event-type identifier."},"created_at":{"type":"string","format":"date-time","description":"Event creation time."}}},"EventPage":{"type":"object","required":["resource","data","next"],"properties":{"resource":{"type":"string","const":"events"},"data":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/Event"}},"next":{"type":["string","null"],"format":"uuid","description":"Pass this cursor as after. Null means this traversal has ended."}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"WebhookEvent":{"type":"object","required":["id","type","createdAt","businessId","locationId","recordId","action"],"properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["repair.updated","job.updated","invoice.paid","customer.created","inventory.changed"]},"createdAt":{"type":"string","format":"date-time"},"businessId":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid"},"recordId":{"type":"string","format":"uuid"},"action":{"type":"string"}}}}}}