{"openapi":"3.0.0","info":{"title":"NexHealth Intent API","description":"> **Beta** — access is granted per developer organization; contact NexHealth to enroll. The intent API is deterministic, agent-facing orchestration: coarse workflow endpoints designed to be called by AI agents on behalf of patients.\n\nThis surface is **unversioned** — do not send the Nex-Api-Version header (requests carrying it are rejected). Changes during the beta are additive. These docs appear alongside the versioned REST reference for convenience; the site's version selector does not apply to the intent API.\n\nBusiness-level failures are returned as data, not HTTP errors: every endpoint responds 200 with `{ \"ok\": false, \"reason\": ..., \"message\": ... }` on a soft failure. `message` is always safe to relay to the end user as-is; branch agent behavior on `reason` using the per-endpoint reason catalogs below.","termsOfService":"https://www.nexhealth.com/terms-of-service","contact":{"name":"NexHealth","email":"info@nexhealth.com"},"license":{"name":"NexHealth License 1.0","url":"https://www.nexhealth.com/privacy"},"version":"intent-beta"},"security":[{"Authorization":[]}],"tags":[{"name":"intent","description":"Deterministic, agent-facing orchestration endpoints: find granted practice locations, then search bookable appointment slots."}],"paths":{"/intent/locations/search":{"post":{"summary":"Find the callers practices by location","description":"Returns granted practice locations near a point, ranked by distance.\nSoft failures return `{ ok: false, reason:, message: }` rather than an HTTP error.\n\n### Example request\n\n```\ncurl -X POST https://nexhealth.info/intent/locations/search \\\n  -H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \\\n  -d '{ \"location\": { \"zip\": \"98101\" }, \"radius_miles\": 15 }'\n```\n\n### Example success response\n\n```json\n{ \"ok\": true,\n  \"locations\": [\n    { \"location_id\": 42, \"name\": \"Main St Dental\",\n      \"address\": \"1 Main St, Seattle, WA, 98101\", \"distance_miles\": 1.2 }\n  ] }\n```\n\n### Reason catalog (branch on `reason` when `ok` is false)\n\n| `reason` | Meaning | Recommended agent recovery |\n|----------|---------|----------------------------|\n| `location_required` | No location input supplied. | Ask the user for a ZIP, city, or coordinates and re-call. |\n| `location_not_understood` | The input could not be resolved to coordinates. | Ask for a 5-digit US ZIP or lat/lng and re-call. |\n| `no_locations_in_radius` | No granted practices within the radius. | Widen `radius_miles` or ask for a different area. |\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/postIntentLocationsSearch"}}},"required":true},"responses":{"200":{"description":"Find the callers practices by location"}},"tags":["intent"],"operationId":"postIntentLocationsSearch"}},"/intent/booking/search":{"post":{"summary":"Find bookable appointment slots","description":"Granted location + visit reason + time window -> ranked slots with opaque\nslot tokens. Soft failures return `{ ok: false, reason:, message: }` rather than\nHTTP errors - `message` is always present and safe to relay to the caller as-is.\n\n### Example request\n\n```\ncurl -X POST https://nexhealth.info/intent/booking/search \\\n  -H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \\\n  -d '{\n    \"location_id\": 42,\n    \"visit_reason\": \"cleaning\",\n    \"when\": { \"earliest_date\": \"2026-08-10\", \"days\": 7, \"parts_of_day\": [\"morning\"] },\n    \"limit\": 5\n  }'\n```\n\n### Example success response\n\n```json\n{ \"ok\": true,\n  \"slots\": [\n    { \"slot_token\": \"slt_9f2c1b7e4a5d6c8b9f2c1b7e4a5d6c8b\",\n      \"start_local\": \"Mon, Aug 10 · 9:00 AM PDT\",\n      \"start_iso\": \"2026-08-10T09:00:00-07:00\",\n      \"duration_minutes\": 30,\n      \"provider\": \"Dr. Sarah Johnson\",\n      \"location\": { \"location_id\": 42, \"name\": \"Main St Dental\", \"address\": \"1 Main St, Seattle, WA, 98101\" } }\n  ],\n  \"summary\": { \"searched_locations\": 1, \"with_availability\": 1, \"window_days\": 7 } }\n```\n\n### Reason catalog (branch on `reason` when `ok` is false)\n\n| `reason` | Meaning | Recommended agent recovery |\n|----------|---------|----------------------------|\n| `location_required` | No usable `location_id` supplied. | Call `locations/search` first and pass one of its `location_id`s. |\n| `appointment_type_unrecognized` | The visit reason matched nothing bookable. | Offer the user the returned `available_types` and re-call with their choice. |\n| `appointment_type_ambiguous` | The visit reason plausibly matches more than one type (or one match too uncertain to auto-book). | Offer the returned `matching_types`, confirm one with the user, then re-call with it as `visit_reason`. |\n| `no_availability` | No open slots in the window. | If `next_available_date` is present, offer it and re-call with `earliest_date` set to it. |\n| `no_availability_in_requested_times` | Slots exist, but none in the requested `parts_of_day`. | Offer the returned `available_parts_of_day` and re-call with one the user accepts. |\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/postIntentBookingSearch"}}},"required":true},"responses":{"200":{"description":"Find bookable appointment slots"}},"tags":["intent"],"operationId":"postIntentBookingSearch"}},"/intent/booking/book":{"post":{"summary":"Book a slot. Success means NexHealth accepted the booking and queued the EHR insert; check `ehr_writeback`.","description":"Consumes a `slot_token` from booking/search with patient demographics.\n\nA success response means NexHealth accepted the booking and created the\nappointment. The insert into the practice's EHR runs asynchronously afterwards,\nso two independent fields describe the result: `booking_status` (formerly\n`status`) is NexHealth's view (`confirmed`, or `cancelled` if the appointment\nwas later cancelled or removed), and `ehr_writeback` is the EHR insert\n(`pending` here; poll booking/status until it reads `confirmed`, or `failed`\nif the insert died and will not resolve on its own). Practices without an EHR\nintegration have nothing to write back to, so their bookings stay `pending`.\n`message` restates the combined state in one sentence and is always safe to\nrelay to the caller as-is.\n\nIdempotent by contract: retrying with the same `idempotency_key` returns the\noriginal booking with `replay: true` - never a double-booking. Reusing a key\nwith different request data is refused as `idempotency_conflict`.\n\n### Example request\n\n```\ncurl -X POST https://nexhealth.info/intent/booking/book \\\n  -H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \\\n  -d '{\n    \"slot_token\": \"slt_9f2c1b7e4a5d6c8b9f2c1b7e4a5d6c8b\",\n    \"idempotency_key\": \"d290f1ee-6c54-4b01-90e6-d701748f0851\",\n    \"patient\": { \"first_name\": \"Jane\", \"last_name\": \"Doe\",\n                 \"date_of_birth\": \"1990-01-15\",\n                 \"phone_number\": \"5551234567\", \"email\": \"jane@example.com\" },\n    \"note\": \"Prefers morning appointments\"\n  }'\n```\n\n### Example success response\n\n```json\n{ \"ok\": true, \"replay\": false,\n  \"message\": \"Booked in NexHealth. The appointment is queued for insertion into the practice's EHR; poll booking/status until ehr_writeback is confirmed.\",\n  \"booking\": { \"booking_ref\": \"bkg_4d81f30a6c2e49b7a5d81f30a6c2e49b\",\n               \"booking_status\": \"confirmed\",\n               \"ehr_writeback\": \"pending\",\n               \"appointment_id\": 918273,\n               \"start_local\": \"Mon, Aug 10 · 9:00 AM PDT\",\n               \"start_iso\": \"2026-08-10T16:00:00Z\",\n               \"duration_minutes\": 30,\n               \"provider\": \"Dr. Sarah Johnson\",\n               \"location\": { \"location_id\": 42, \"name\": \"Main St Dental\", \"address\": \"1 Main St, Seattle, WA, 98101\" },\n               \"patient\": { \"first_name\": \"Jane\", \"last_name\": \"Doe\" } } }\n```\n\n### Reason catalog (branch on `reason` when `ok` is false)\n\n| `reason` | Meaning | Recommended agent recovery |\n|----------|---------|----------------------------|\n| `slot_not_found` | Unknown or foreign slot token. | Re-run booking/search and book a fresh slot. |\n| `slot_expired` | The slot token's hold window lapsed. | Re-run booking/search and book a fresh slot. |\n| `slot_taken` | Someone else booked this slot first. | Offer the returned `alternatives`, or re-search. |\n| `idempotency_conflict` | This `idempotency_key` is recorded against different request data. | Use a new key for a new request. |\n| `patient_fields_required` | The practice's EHR needs more patient fields to create a new patient. | Collect the returned `missing_fields` and retry. |\n| `new_patient_not_supported` | This practice cannot accept brand-new patients through the API. | Only book patients that already exist at the practice. |\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/postIntentBookingBook"}}},"required":true},"responses":{"200":{"description":"Book a slot. Success means NexHealth accepted the booking and queued the EHR insert; check `ehr_writeback`."}},"tags":["intent"],"operationId":"postIntentBookingBook"}},"/intent/booking/status":{"get":{"summary":"Check a booking, including whether its EHR insert has completed (`ehr_writeback`).","description":"Booking status by booking_ref, as two independent fields. `booking_status`\n(formerly `status`) is `confirmed` while the booking stands and `cancelled` if\nthe appointment was cancelled or removed after booking. `ehr_writeback` is\n`pending` until the appointment reaches the practice's EHR, `confirmed` once it\nhas, `failed` if the sync to the practice's EHR permanently failed (contact\nsupport - this will not resolve on its own), and `unknown` if the appointment\nrecord can no longer be found (stop polling and verify with the practice).\nPractices without an EHR integration have nothing to write back to, so their\nbookings stay `pending`. `message` restates the combined state in one sentence\nthat is safe to relay as-is. `start_iso` tracks the live appointment time (a\npractice-side reschedule moves it; `start_local` stays as booked).\n\n### Example request\n\n```\ncurl 'https://nexhealth.info/intent/booking/status?booking_ref=bkg_4d81f30a6c2e49b7a5d81f30a6c2e49b' \\\n  -H 'Authorization: Bearer <token>'\n```\n\n### Example success response\n\n```json\n{ \"ok\": true,\n  \"message\": \"Booked in NexHealth. The appointment is queued for insertion into the practice's EHR; poll booking/status until ehr_writeback is confirmed.\",\n  \"booking\": { \"booking_ref\": \"bkg_4d81f30a6c2e49b7a5d81f30a6c2e49b\",\n               \"booking_status\": \"confirmed\",\n               \"ehr_writeback\": \"pending\",\n               \"appointment_id\": 918273,\n               \"start_local\": \"Mon, Aug 10 · 9:00 AM PDT\",\n               \"start_iso\": \"2026-08-10T16:00:00Z\",\n               \"duration_minutes\": 30,\n               \"provider\": \"Dr. Sarah Johnson\",\n               \"location\": { \"location_id\": 42, \"name\": \"Main St Dental\", \"address\": \"1 Main St, Seattle, WA, 98101\" },\n               \"patient\": { \"first_name\": \"Jane\", \"last_name\": \"Doe\" } } }\n```\n\n### Reason catalog (branch on `reason` when `ok` is false)\n\n| `reason` | Meaning | Recommended agent recovery |\n|----------|---------|----------------------------|\n| `booking_not_found` | No booking with this reference is visible to your organization. | Verify the `booking_ref` came from your own booking/book response; there is nothing to retry. |\n","parameters":[{"in":"query","name":"booking_ref","description":"A booking_ref returned by booking/book.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Check a booking, including whether its EHR insert has completed (`ehr_writeback`)."}},"tags":["intent"],"operationId":"getIntentBookingStatus"}}},"servers":[{"url":"https://nexhealth.info"}],"components":{"securitySchemes":{"Authorization":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"postIntentLocationsSearch":{"type":"object","properties":{"location":{"type":"object","properties":{"text":{"type":"string","description":"City, neighbourhood, or ZIP to search near (5-digit ZIP only)."},"zip":{"type":"string","description":"US ZIP code."},"lat":{"type":"number","format":"float","description":"WGS-84 latitude; supply with lng."},"lng":{"type":"number","format":"float","description":"WGS-84 longitude; supply with lat."}}},"radius_miles":{"type":"number","format":"float","description":"Search radius in miles (default 20, must be positive)."}},"description":"Find the callers practices by location"},"postIntentBookingSearch":{"type":"object","properties":{"location_id":{"type":"integer","format":"int32","description":"A location_id returned by locations/search."},"visit_reason":{"type":"string","description":"Free text, e.g. \"cleaning\" or \"checkup\"."},"when":{"type":"object","properties":{"earliest_date":{"type":"string","format":"date","description":"First acceptable date (default today)."},"days":{"type":"integer","format":"int32","description":"Window size in days (default 7, max 14)."},"parts_of_day":{"type":"array","description":"Filter to parts of the day (location-local).","items":{"type":"string","enum":["morning","afternoon","evening"]}}}},"limit":{"type":"integer","format":"int32","description":"Max slots to return (default 5, max 10)."}},"required":["location_id"],"description":"Find bookable appointment slots"},"postIntentBookingBook":{"type":"object","properties":{"slot_token":{"type":"string","description":"A slot_token returned by booking/search."},"idempotency_key":{"type":"string","description":"Caller-chosen key making this booking retry-safe; one key per booking attempt."},"patient":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"date_of_birth":{"type":"string","format":"date"},"phone_number":{"type":"string"},"email":{"type":"string"},"gender":{"type":"string"}},"required":["first_name","last_name","date_of_birth"]},"note":{"type":"string","description":"Free-text note attached to the appointment."}},"required":["slot_token","idempotency_key","patient"],"description":"Book a slot. Success means NexHealth accepted the booking and queued the EHR insert; check `ehr_writeback`."}}}}