{"components":{"schemas":{"Address":{"properties":{"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street"}},"title":"Address","type":"object"},"ErrorBody":{"properties":{"code":{"description":"Machine-readable error code.","examples":["NOT_FOUND"],"title":"Code","type":"string"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Optional structured error context.","title":"Details"},"message":{"description":"Human-readable error message.","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorBody","type":"object"},"ErrorResponse":{"properties":{"error":{"$ref":"#/components/schemas/ErrorBody"},"meta":{"anyOf":[{"$ref":"#/components/schemas/Meta"},{"type":"null"}]},"success":{"default":false,"description":"Always false for errors.","title":"Success","type":"boolean"}},"required":["error"],"title":"ErrorResponse","type":"object"},"LinkBroken":{"description":"Why the tracker link is broken. Present only when it is.","properties":{"reason":{"description":"conflict: the tracker contradicts the odometer reading. no_reading: no odometer reading from the tracker. stale: the latest reading is older than 7 days.","enum":["conflict","no_reading","stale"],"title":"Reason","type":"string"},"since":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 timestamp of the last odometer reading (conflict, stale); null for no_reading.","title":"Since"},"tracker_km":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"What the tracker reports, in km (conflict only).","title":"Tracker Km"}},"required":["reason"],"title":"LinkBroken","type":"object"},"Meta":{"description":"Response metadata. List endpoints additionally populate pagination keys.","properties":{"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Page size used for this response.","title":"Limit"},"offset":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Offset used for this response.","title":"Offset"},"timestamp":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 server timestamp.","title":"Timestamp"},"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Total matching records (list endpoints).","title":"Total"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"API contract version.","title":"Version"}},"title":"Meta","type":"object"},"Run":{"description":"A service run (job/work order).","example":{"appointment":{"confirmed_at":null,"preferred_end":"2026-06-01T17:00:00Z","preferred_start":"2026-06-01T15:00:00Z"},"created_at":"2026-05-20T12:00:00Z","customer":{"address":null,"email":"jordan@example.com","name":"Jordan Lee","phone":"+15125550123"},"description":"Customer reported squeaking.","id":"run_9f1c...","items":[{"description":null,"status":"pending","title":"Synthetic oil change","type":"service"}],"reference_code":"HR-AB23CD45EF67","scheduling_state":"requested","scheduling_status":"pending","status":"open","title":"Oil change + brake inspection","updated_at":"2026-05-21T09:30:00Z","vehicle":{"id":"veh_123","license_plate":"ABC1234","make":"Honda","model":"Accord","vin":"1HGCM82633A004352","year":2021}},"properties":{"appointment":{"anyOf":[{"$ref":"#/components/schemas/RunAppointment"},{"type":"null"}]},"appointment_time":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Mutable current candidate or confirmed time specification.","title":"Appointment Time"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"customer":{"anyOf":[{"$ref":"#/components/schemas/RunCustomer"},{"type":"null"}]},"customer_requested_time":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Immutable semantic time specification captured at submission.","title":"Customer Requested Time"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"items":{"items":{"$ref":"#/components/schemas/RunItem"},"title":"Items","type":"array"},"reference_code":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stable human-facing HoneyRuns case reference. Not an authorization secret.","title":"Reference Code"},"scheduling_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Canonical scheduling lifecycle: requested | proposed | confirmed | cancelled.","title":"Scheduling State"},"scheduling_status":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Deprecated; computed from scheduling_state (requested -> pending, proposed, confirmed; cancelled -> null).","title":"Scheduling Status"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Run status: open | in_progress | completed | dismissed.","title":"Status"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"vehicle":{"anyOf":[{"$ref":"#/components/schemas/RunVehicle"},{"type":"null"}]}},"title":"Run","type":"object"},"RunAppointment":{"properties":{"confirmed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 confirmed appointment time.","title":"Confirmed At"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Current candidate or confirmed window end.","title":"End"},"preferred_end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 requested window end.","title":"Preferred End"},"preferred_start":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 requested window start.","title":"Preferred Start"},"start":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Current candidate or confirmed window start.","title":"Start"}},"title":"RunAppointment","type":"object"},"RunCustomer":{"properties":{"address":{"anyOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}]},"display_address":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Flat display string for the service location (canonical or legacy flat address). Additive to the structured ``address`` object; null when no flat address is available.","title":"Display Address"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"}},"title":"RunCustomer","type":"object"},"RunItem":{"properties":{"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Line-item type, e.g. service or inspection.","title":"Type"}},"title":"RunItem","type":"object"},"RunListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Run"},"type":"array"},"meta":{"$ref":"#/components/schemas/Meta"},"success":{"example":true,"type":"boolean"}},"required":["success","data"],"type":"object"},"RunResponse":{"properties":{"data":{"$ref":"#/components/schemas/Run"},"meta":{"$ref":"#/components/schemas/Meta"},"success":{"example":true,"type":"boolean"}},"required":["success","data"],"type":"object"},"RunVehicle":{"description":"Minimal vehicle reference embedded in a run.","properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"license_plate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License Plate"},"make":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Make"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"vin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vin"},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},"title":"RunVehicle","type":"object"},"Severity":{"enum":["info","low","medium","high","critical"],"title":"Severity","type":"string"},"Vehicle":{"description":"A vehicle with its latest telematics-derived state.","example":{"as_of":"2026-05-24T08:00:00Z","health":{"alerts":[{"display_name":"Engine Oil & Filter","due_date":"2026-06-10","due_miles_remaining":120,"severity":"high","type":"oil_change"}],"status":"needs_attention"},"id":"veh_123","license_plate":"ABC1234","link_broken":null,"make":"Honda","model":"Accord","name":"Truck 7","signals":{"fuel_percent":62,"odometer":{"as_of":"2026-05-24T08:00:00Z","km":84210.0,"miles":52325.0}},"telematics_provider":"dimo","updated_at":"2026-05-24T08:00:01Z","vin":"1HGCM82633A004352","year":2021},"properties":{"as_of":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 timestamp of the underlying state.","title":"As Of"},"health":{"anyOf":[{"$ref":"#/components/schemas/VehicleHealth"},{"type":"null"}]},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"license_plate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License Plate"},"link_broken":{"anyOf":[{"$ref":"#/components/schemas/LinkBroken"},{"type":"null"}],"description":"Set when the vehicle has a telematics provider but no current odometer from it: no reading, a reading older than 7 days, or a reading the provider contradicts; the object says which. Always null for manually tracked vehicles and for a healthy link."},"make":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Make"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"signals":{"additionalProperties":true,"description":"Provider-normalized signals (odometer, fuel, battery, etc.). Keys vary by provider; values are objects or scalars.","title":"Signals","type":"object"},"telematics_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Source provider, e.g. dimo | samsara | bouncie.","title":"Telematics Provider"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"vin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vin"},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},"title":"Vehicle","type":"object"},"VehicleAlert":{"description":"A health alert: maintenance-due or an abnormal telematics signal.","properties":{"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 date the interval is due.","title":"Due Date"},"due_miles_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Miles until the maintenance interval is due (maintenance alerts).","title":"Due Miles Remaining"},"severity":{"$ref":"#/components/schemas/Severity","description":"SSOT severity: info | low | medium | high | critical."},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Interval type or signal name.","title":"Type"}},"required":["severity"],"title":"VehicleAlert","type":"object"},"VehicleHealth":{"properties":{"alerts":{"items":{"$ref":"#/components/schemas/VehicleAlert"},"title":"Alerts","type":"array"},"status":{"description":"Roll-up health: ok | needs_attention | critical | unknown.","title":"Status","type":"string"}},"required":["status"],"title":"VehicleHealth","type":"object"},"VehicleListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Vehicle"},"type":"array"},"meta":{"$ref":"#/components/schemas/Meta"},"success":{"example":true,"type":"boolean"}},"required":["success","data"],"type":"object"},"VehicleRefreshRequest":{"properties":{"cascade":{"default":true,"description":"Whether to run downstream processing (alerts/runs) after refresh.","title":"Cascade","type":"boolean"}},"title":"VehicleRefreshRequest","type":"object"},"VehicleResponse":{"properties":{"data":{"$ref":"#/components/schemas/Vehicle"},"meta":{"$ref":"#/components/schemas/Meta"},"success":{"example":true,"type":"boolean"}},"required":["success","data"],"type":"object"},"VehicleUpdateRequest":{"additionalProperties":false,"description":"Writable vehicle fields. The contract is intentionally narrow.","properties":{"license_plate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License Plate"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"odometer_miles":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Odometer reading in miles, recorded as a reading taken now.","title":"Odometer Miles"}},"title":"VehicleUpdateRequest","type":"object"},"WebhookEnvelope":{"description":"Body of every webhook delivery. `data` is the run in the same shape as `GET /runs/{run_id}` plus handoff fields (`owner_company`, `service_location`, `customer.id/first_name/last_name`, `appointment.external_id`) so a receiving system can create or update a customer, job and appointment from one event. The `webhook.test` event sent by *Send test* carries a fully synthetic run in this same shape.","properties":{"api_version":{"description":"Bumped when the envelope shape or signing scheme changes.","example":"2026-06-11","type":"string"},"data":{"$ref":"#/components/schemas/WebhookRun"},"event":{"enum":["run.appointment_confirmed","run.appointment_updated","run.appointment_cancelled","run.dispatch_proposed","run.created","run.completed","webhook.test"],"example":"run.completed","type":"string"},"event_id":{"description":"Stable per event; a retried delivery re-sends the same id. Deduplicate on it.","example":"a3b1c9f2-6d4e-4c1a-9b7f-2e8d1f0a5c33","type":"string"},"timestamp":{"example":"2026-09-02T18:41:07Z","format":"date-time","type":"string"}},"required":["event","event_id","api_version","timestamp","data"],"type":"object"},"WebhookRun":{"description":"The run as delivered in every `run.*` webhook: `Run` plus handoff fields.\n\nBackward compatible \u2014 every `Run` field is unchanged; the additions let a\nreceiving system create/update a customer, job and appointment from one event.","example":{"appointment":{"confirmed_at":null,"end":"2026-06-01T17:00:00Z","external_id":"run_9f1c...","preferred_end":"2026-06-01T17:00:00Z","preferred_start":"2026-06-01T15:00:00Z","start":"2026-06-01T15:00:00Z"},"created_at":"2026-05-20T12:00:00Z","customer":{"address":null,"display_address":"100 Example Way, Suite 200, Austin, TX 78701","email":"jordan@example.com","first_name":"Jordan","id":"ct_5e9f...","last_name":"Lee","name":"Jordan Lee","phone":"+15125550123"},"description":"Customer reported squeaking.","id":"run_9f1c...","items":[{"description":null,"status":"pending","title":"Synthetic oil change","type":"service"}],"owner_company":{"external_ids":{"dispatch":"386563"},"id":"co_7d2a...","name":"Acme Fleet LLC"},"reference_code":"HR-AB23CD45EF67","scheduling_state":"requested","scheduling_status":"pending","service_location":{"city":"Austin","country":"US","display_address":"100 Example Way, Suite 200, Austin, TX 78701","id":"loc_41b0...","name":"North Yard","postal_code":"78701","source":"run","state":"TX","street_1":"100 Example Way","street_2":"Suite 200"},"status":"open","title":"Oil change + brake inspection","updated_at":"2026-05-21T09:30:00Z","vehicle":{"id":"veh_123","license_plate":"ABC1234","make":"Honda","model":"Accord","vin":"1HGCM82633A004352","year":2021}},"properties":{"appointment":{"anyOf":[{"$ref":"#/components/schemas/WebhookRunAppointment"},{"type":"null"}]},"appointment_time":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Mutable current candidate or confirmed time specification.","title":"Appointment Time"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"customer":{"anyOf":[{"$ref":"#/components/schemas/WebhookRunCustomer"},{"type":"null"}]},"customer_requested_time":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Immutable semantic time specification captured at submission.","title":"Customer Requested Time"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"items":{"items":{"$ref":"#/components/schemas/RunItem"},"title":"Items","type":"array"},"owner_company":{"anyOf":[{"$ref":"#/components/schemas/WebhookRunOwnerCompany"},{"type":"null"}]},"reference_code":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stable human-facing HoneyRuns case reference. Not an authorization secret.","title":"Reference Code"},"scheduling_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Canonical scheduling lifecycle: requested | proposed | confirmed | cancelled.","title":"Scheduling State"},"scheduling_status":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Deprecated; computed from scheduling_state (requested -> pending, proposed, confirmed; cancelled -> null).","title":"Scheduling Status"},"service_location":{"anyOf":[{"$ref":"#/components/schemas/WebhookRunServiceLocation"},{"type":"null"}]},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Run status: open | in_progress | completed | dismissed.","title":"Status"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"vehicle":{"anyOf":[{"$ref":"#/components/schemas/RunVehicle"},{"type":"null"}]}},"title":"WebhookRun","type":"object"},"WebhookRunAppointment":{"properties":{"confirmed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 confirmed appointment time.","title":"Confirmed At"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Current candidate or confirmed window end.","title":"End"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stable external appointment id \u2014 the run id (one run \u2194 one appointment). Fixed across re-proposals of the same run.","title":"External Id"},"preferred_end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 requested window end.","title":"Preferred End"},"preferred_start":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 requested window start.","title":"Preferred Start"},"start":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Current candidate or confirmed window start.","title":"Start"}},"title":"WebhookRunAppointment","type":"object"},"WebhookRunCustomer":{"properties":{"address":{"anyOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}]},"display_address":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Flat display string for the service location (canonical or legacy flat address). Additive to the structured ``address`` object; null when no flat address is available.","title":"Display Address"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Derived: first token of `name`.","title":"First Name"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"HoneyRuns contact id.","title":"Id"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Derived: remainder of `name`.","title":"Last Name"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"}},"title":"WebhookRunCustomer","type":"object"},"WebhookRunOwnerCompany":{"description":"The company that OWNS the run (the fleet/customer, never the servicing shop).","properties":{"external_ids":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"description":"Ids the servicing shop keeps for this customer in its own tools, keyed by connector \u2014 e.g. {\"dispatch\": \"386563\"}. Match on these before creating a customer.","title":"External Ids"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stable external customer id for a dispatch board.","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"title":"WebhookRunOwnerCompany","type":"object"},"WebhookRunServiceLocation":{"description":"Where the work happens. Always present, even when the run has no contact.\n\n``display_address`` is the flat address as entered (or assembled from the parts\nfor structured-only records). ``street_1``/``city``/``state``/``postal_code``/\n``country`` are a conservative best-effort split \u2014 a part the splitter cannot\nplace with confidence is ``null``, never guessed.","properties":{"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 3166-1 alpha-2 when derivable.","title":"Country"},"display_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Address"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Canonical service_locations id, when linked.","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Yard/lot label, e.g. `North Yard`.","title":"Name"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"source":{"anyOf":[{"enum":["run","vehicle_home","learned","legacy"],"type":"string"},{"type":"null"}],"description":"Where the address came from: `run` (stamped on the run by a booking, a schedule edit or the location backfill), `vehicle_home` (the vehicle's home location), `learned` (where this vehicle was last serviced), `legacy` (free-text copy). Null when no address is known. Gate on it if you only want addresses recorded on the run itself.","title":"Source"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"street_1":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street 1"},"street_2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street 2"}},"title":"WebhookRunServiceLocation","type":"object"}},"securitySchemes":{"bearerAuth":{"description":"Your HoneyRuns API key. Create one in the HoneyRuns app under Settings \u2192 Developers (/admin/developers).","scheme":"bearer","type":"http"}}},"externalDocs":{"description":"Developer docs","url":"https://docs.honeyruns.com"},"info":{"contact":{"email":"support@honeyruns.com","name":"HoneyRuns Support"},"description":"Read your HoneyRuns data \u2014 service runs and vehicle telematics \u2014 and request\nservice, from external tools and automations. One standardized API across every\ntelematics provider.\n\n**Base URL:** `https://api.honeyruns.com/api/v1` \u00b7 **Spec:** [`/openapi.json`](https://docs.honeyruns.com/openapi.json)\n\u00b7 **For agents:** [`/llms.txt`](https://docs.honeyruns.com/llms.txt)\n\n## Authentication\n\nPass your API key as a bearer token on every request:\n\n```\nAuthorization: Bearer YOUR_API_KEY\n```\n\nKeys are self-serve \u2014 create and revoke them in the HoneyRuns app under\n**Settings \u2192 Developers** (`/admin/developers`).\n\n## Responses\n\nEvery response uses one envelope:\n\n```json\n{ \"success\": true,  \"data\": ..., \"meta\": ... }\n{ \"success\": false, \"error\": { \"code\": \"...\", \"message\": \"...\" } }\n```\n\nList endpoints paginate with `limit` / `offset`.\n\n## Polling with `updated_since`\n\nFor Zapier and other polling integrations, pass the `updated_at` of the newest\nrecord you have seen as `updated_since` on the next poll. The filter is\ninclusive (`updated_at >= updated_since`), so expect the cursor record again\nand dedupe on `id`. Results are newest-first; page through every `offset` before\nadvancing the cursor. Works on `GET /runs` and `GET /vehicles`.\n\n```\ncurl \"https://api.honeyruns.com/api/v1/runs?updated_since=2026-03-15T10:00:00Z\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n## Webhooks\n\nSubscribe under **Settings \u2192 Developers**: an HTTPS URL plus the events you\nwant. You receive the signing secret once, at creation. Every delivery is a\n`POST` of a `WebhookEnvelope`; the events are listed in the **Webhooks** section\nof this reference.\n\n### Verifying signatures\n\nEach delivery carries `HoneyRuns-Signature: t=<unix-seconds>,v1=<hex>` where\n`v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` using your signing secret.\nRecompute, compare constant-time, and reject timestamps older than a few minutes.\n\n```python\nimport hmac, hashlib, time\n\ndef verify(secret: str, header: str, raw_body: bytes, tolerance: int = 300) -> bool:\n    parts = dict(p.split(\"=\", 1) for p in header.split(\",\"))\n    if abs(time.time() - int(parts[\"t\"])) > tolerance:\n        return False\n    signed = f\"{parts['t']}.\".encode() + raw_body\n    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, parts[\"v1\"])\n```\n\nDeliveries are **not retried**: a non-2xx response or timeout is recorded as a\nfailed delivery. Treat webhooks as the fast path and polling with `updated_since`\nas the catch-up path. Handle events idempotently using `event_id`.\n\nQuestions? [support@honeyruns.com](mailto:support@honeyruns.com)\n","title":"HoneyRuns Public API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/runs":{"get":{"description":"Return a paginated list of service runs for the authenticated company, newest activity first.","operationId":"listRuns","parameters":[{"description":"Filter by run status.","in":"query","name":"status","schema":{"enum":["open","in_progress","completed","dismissed"],"type":"string"}},{"description":"Filter by the canonical scheduling lifecycle.","in":"query","name":"scheduling_state","schema":{"enum":["requested","proposed","confirmed","cancelled"],"type":"string"}},{"deprecated":true,"description":"Deprecated; maps onto scheduling_state (pending and same_day_requested -> requested).","in":"query","name":"scheduling_status","schema":{"enum":["confirmed","pending","proposed","same_day_requested"],"type":"string"}},{"description":"ISO 8601 datetime \u2014 return only records with `updated_at` at or after this time (inclusive). Use this to poll for changes (Zapier, sync jobs).","in":"query","name":"updated_since","schema":{"format":"date-time","type":"string"}},{"description":"Results per page (1\u2013200).","in":"query","name":"limit","schema":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},{"description":"Results to skip.","in":"query","name":"offset","schema":{"default":0,"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunListResponse"}}},"description":"A page of runs."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid query parameter."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key."}},"security":[{"bearerAuth":[]}],"summary":"List runs","tags":["Runs"]}},"/runs/{run_id}":{"get":{"operationId":"getRun","parameters":[{"in":"path","name":"run_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunResponse"}}},"description":"The run."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Run not found."}},"security":[{"bearerAuth":[]}],"summary":"Get a run","tags":["Runs"]}},"/vehicles":{"get":{"description":"Return active vehicles for the authenticated company with their latest telematics-derived state and health.","operationId":"listVehicles","parameters":[{"description":"ISO 8601 datetime \u2014 return only records with `updated_at` at or after this time (inclusive). Use this to poll for changes (Zapier, sync jobs).","in":"query","name":"updated_since","schema":{"format":"date-time","type":"string"}},{"description":"Results per page (1\u2013200).","in":"query","name":"limit","schema":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},{"description":"Results to skip.","in":"query","name":"offset","schema":{"default":0,"minimum":0,"type":"integer"}},{"description":"Include the total count in `meta` (extra query cost).","in":"query","name":"include_total","schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleListResponse"}}},"description":"A page of vehicles."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid query parameter."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key."}},"security":[{"bearerAuth":[]}],"summary":"List vehicles","tags":["Vehicles"]}},"/vehicles/{vehicle_id}":{"get":{"operationId":"getVehicle","parameters":[{"in":"path","name":"vehicle_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleResponse"}}},"description":"The vehicle."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Vehicle not found."}},"security":[{"bearerAuth":[]}],"summary":"Get a vehicle","tags":["Vehicles"]},"put":{"description":"Update a narrow set of writable fields. An odometer write is recorded as a reading taken now.","operationId":"updateVehicle","parameters":[{"in":"path","name":"vehicle_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleResponse"}}},"description":"The updated vehicle."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"No valid fields, or a validation error."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Vehicle not found."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Odometer cannot be updated for this vehicle/source."}},"security":[{"bearerAuth":[]}],"summary":"Update a vehicle","tags":["Vehicles"]}},"/vehicles/{vehicle_id}/refresh":{"post":{"description":"Trigger an on-demand pull of the latest data from the vehicle's telematics provider. Response shape is provider-specific.","operationId":"refreshVehicle","parameters":[{"in":"path","name":"vehicle_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleRefreshRequest"}}},"required":false},"responses":{"200":{"description":"Refresh initiated (provider-specific payload)."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Provider does not support refresh."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Vehicle not found."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Provider refresh not yet implemented."}},"security":[{"bearerAuth":[]}],"summary":"Refresh a vehicle from its telematics provider","tags":["Vehicles"]}}},"security":[{"bearerAuth":[]}],"servers":[{"description":"Production","url":"https://api.honeyruns.com/api/v1"}],"tags":[{"description":"Service runs \u2014 appointments, work orders, and their outcomes. Poll with `updated_since` (see *Polling*).","name":"Runs"},{"description":"Vehicles with their latest telematics-derived state and health alerts. Update the writable fields or trigger a provider refresh.","name":"Vehicles"},{"description":"Outbound events HoneyRuns POSTs to your subscribed URL (see *Webhooks* for subscribing and signature verification).","name":"Webhooks"}],"webhooks":{"run.appointment_cancelled":{"post":{"description":"A pending, proposed, or confirmed appointment was cancelled.","operationId":"webhook_run_appointment_cancelled","parameters":[{"description":"`t=<unix-seconds>,v1=<hex>` \u2014 `v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` with your signing secret. See *Verifying signatures*.","in":"header","name":"HoneyRuns-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}},"required":true},"responses":{"2XX":{"description":"Acknowledged. A non-2xx status or a timeout is recorded as a failed delivery; it is not retried \u2014 catch up by polling with `updated_since`."}},"security":[],"summary":"run.appointment_cancelled","tags":["Webhooks"]}},"run.appointment_confirmed":{"post":{"description":"An appointment was confirmed for the run.","operationId":"webhook_run_appointment_confirmed","parameters":[{"description":"`t=<unix-seconds>,v1=<hex>` \u2014 `v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` with your signing secret. See *Verifying signatures*.","in":"header","name":"HoneyRuns-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}},"required":true},"responses":{"2XX":{"description":"Acknowledged. A non-2xx status or a timeout is recorded as a failed delivery; it is not retried \u2014 catch up by polling with `updated_since`."}},"security":[],"summary":"run.appointment_confirmed","tags":["Webhooks"]}},"run.appointment_updated":{"post":{"description":"The shop proposed or re-proposed an appointment time (the run is now `proposed`, awaiting confirmation).","operationId":"webhook_run_appointment_updated","parameters":[{"description":"`t=<unix-seconds>,v1=<hex>` \u2014 `v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` with your signing secret. See *Verifying signatures*.","in":"header","name":"HoneyRuns-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}},"required":true},"responses":{"2XX":{"description":"Acknowledged. A non-2xx status or a timeout is recorded as a failed delivery; it is not retried \u2014 catch up by polling with `updated_since`."}},"security":[],"summary":"run.appointment_updated","tags":["Webhooks"]}},"run.completed":{"post":{"description":"The run made an open\u2192completed transition. A run that is reopened and completed again fires again \u2014 treat it as a transition, not a once-per-run guarantee.","operationId":"webhook_run_completed","parameters":[{"description":"`t=<unix-seconds>,v1=<hex>` \u2014 `v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` with your signing secret. See *Verifying signatures*.","in":"header","name":"HoneyRuns-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}},"required":true},"responses":{"2XX":{"description":"Acknowledged. A non-2xx status or a timeout is recorded as a failed delivery; it is not retried \u2014 catch up by polling with `updated_since`."}},"security":[],"summary":"run.completed","tags":["Webhooks"]}},"run.created":{"post":{"description":"A run came into existence \u2014 from your service request or any other source. Fires once; never for a request folded into an existing open run.","operationId":"webhook_run_created","parameters":[{"description":"`t=<unix-seconds>,v1=<hex>` \u2014 `v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` with your signing secret. See *Verifying signatures*.","in":"header","name":"HoneyRuns-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}},"required":true},"responses":{"2XX":{"description":"Acknowledged. A non-2xx status or a timeout is recorded as a failed delivery; it is not retried \u2014 catch up by polling with `updated_since`."}},"security":[],"summary":"run.created","tags":["Webhooks"]}},"run.dispatch_proposed":{"post":{"description":"Riggs proposed sending the run to a shop or technician. A proposal, not a command: the receiving system's human converts it.","operationId":"webhook_run_dispatch_proposed","parameters":[{"description":"`t=<unix-seconds>,v1=<hex>` \u2014 `v1` is HMAC-SHA256 over `\"{t}.{raw_body}\"` with your signing secret. See *Verifying signatures*.","in":"header","name":"HoneyRuns-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}},"required":true},"responses":{"2XX":{"description":"Acknowledged. A non-2xx status or a timeout is recorded as a failed delivery; it is not retried \u2014 catch up by polling with `updated_since`."}},"security":[],"summary":"run.dispatch_proposed","tags":["Webhooks"]}}}}
