openapi: 3.1.0 info: title: HealthSherpa Public Enrollment Sessions Reference API version: 0.1.0 description: 'Machine-readable contract for the currently available public HealthSherpa endpoints: GET /v1/ping, GET /v1/reference/counties, GET /v1/reference/issuers, GET /v1/reference/providers, POST /v1/quotes, the approval-gated POST /v1/enrollment-sessions, and the approval-gated direct enrollment endpoints GET /v1/enrollments, POST /v1/enrollments, PUT /v1/enrollments/{enrollment_id}, GET /v1/enrollments/{enrollment_id}, POST /v1/enrollments/{enrollment_id}/cancellations, POST /v1/enrollments/{enrollment_id}/terminations, POST /v1/enrollments/{enrollment_id}/submissions, GET /v1/enrollments/{enrollment_id}/payment_redirect, and POST /v1/enrollments/{enrollment_id}/supporting_documentation. Object schemas remain additive and clients should ignore response fields they do not recognize. Most HealthSherpa-owned errors follow the unified `ErrorResponse` schema: `{ "error": { "code": string, "message": string, "details"?: object } }`. The direct enrollment endpoints instead return service failures as a top-level `errors[]` array: `{ "errors": [ { "code": string, "message": string, "field"?: string } ] }`. Edge-generated failures (API Gateway and WAF, before a request reaches the service) always use the unified `ErrorResponse` envelope, including on the direct enrollment endpoints — most notably a missing or invalid API key (`403 forbidden`).' servers: - url: https://api.one.healthsherpa.com description: Production security: - ApiKeyAuth: [] tags: - name: Reference paths: /v1/reference/counties: get: tags: - Reference summary: List counties for a ZIP code description: Returns the counties that overlap the supplied ZIP code. A ZIP code can cross state lines; each returned county's `state` is the canonical state for its `fips_code`. Use the selected fips_code in subsequent quote requests. operationId: listCounties parameters: - name: zip_code in: query required: true description: Five-digit ZIP code to resolve. schema: type: string pattern: ^[0-9]{5}$ responses: '200': description: One or more counties found for the ZIP code. content: application/json: schema: $ref: '#/components/schemas/CountiesResponse' example: counties: - fips_code: '12086' name: Miami-Dade County state: FL '400': $ref: '#/components/responses/BadRequest' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '502': $ref: '#/components/responses/BadGateway' '503': $ref: '#/components/responses/ServiceUnavailable' '504': $ref: '#/components/responses/GatewayTimeout' /v1/reference/issuers: get: tags: - Reference summary: List QHP issuers active in a state description: 'Returns the QHP issuers active in the requested state and plan year. Each entry combines a sortable display name (issuer marketing name when present, legal name otherwise) with the canonical 5-digit HIOS issuer ID for use in subsequent quote filters. **Plan year:** the optional `plan_year` query parameter pins the lookup to a specific year and defaults to the current ACA plan year (open enrollment for 2027 begins November 1 2026). Specify `plan_year` explicitly when reconciling enrollments from a prior year or when shopping the next year before the rollover. **Response ordering:** entries are sorted by `name` (case-insensitive) and then by `hios_issuer_id` as a stable tiebreaker. Clients can iterate the array directly without re-sorting.' operationId: listIssuers parameters: - name: state in: query required: true description: Two-letter uppercase US state code (e.g. `FL`). Lowercase values are rejected with `400 invalid_request` — clients must upcase before sending. example: FL schema: type: string pattern: ^[A-Z]{2}$ - name: plan_year in: query required: false description: ACA plan year to scope the issuer list to. Defaults to the current ACA plan year when omitted. schema: type: integer minimum: 2020 maximum: 2099 example: 2026 responses: '200': description: One or more issuers found for the state. content: application/json: schema: $ref: '#/components/schemas/IssuersResponse' example: issuers: - name: Gulf Coast Coverage Co. hios_issuer_id: '33333' - name: Sunshine State Health hios_issuer_id: '12345' '400': $ref: '#/components/responses/BadRequest' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '502': $ref: '#/components/responses/BadGateway' '503': $ref: '#/components/responses/ServiceUnavailable' '504': $ref: '#/components/responses/GatewayTimeout' /v1/reference/providers: get: tags: - Reference summary: Search healthcare providers by name near a ZIP code description: 'Searches healthcare providers (individuals and facilities) by name near a ZIP code and returns paginated results with NPI and address data. This is a search-only endpoint; there is no "list all providers" mode. Use the returned 10-digit `npi` values when building provider selections, for example the `providers` field on an enrollment session. **Channel:** the required `exchange` parameter selects the marketplace channel (`on_exchange` or `off_exchange`). **Pagination:** results are paginated with the nested `page[number]` and `page[size]` query parameters, mirroring the quoting endpoint''s `page` object. `page[number]` defaults to 1 (values below 1 are treated as 1) and `page[size]` accepts 1 to 50, defaulting to 50 (values outside the range resolve to 50). Pagination metadata is returned under `meta` (`page_number`, `page_size`, `result_count`). An empty search returns `providers: []` with `result_count` 0.' operationId: searchProviders parameters: - name: query in: query required: true description: Provider or facility name search term. schema: type: string minLength: 1 example: Smith - name: zip_code in: query required: true description: Five-digit ZIP code used for the geographic (near-ZIP) search. schema: type: string pattern: ^[0-9]{5}$ example: '85001' - name: exchange in: query required: true description: Marketplace channel to search. Required. One of `on_exchange` or `off_exchange`. schema: type: string enum: - on_exchange - off_exchange example: on_exchange - name: page[number] in: query required: false description: Page number (mirrors quoting's page.number). Defaults to 1; values below 1 are treated as 1. schema: type: integer minimum: 1 default: 1 example: 1 - name: page[size] in: query required: false description: Results per page (mirrors quoting's page.size), from 1 to 50. Defaults to 50; values outside this range resolve to 50. schema: type: integer minimum: 1 maximum: 50 default: 50 example: 50 responses: '200': description: Provider search results for the current page. An empty search returns an empty `providers` array. content: application/json: schema: $ref: '#/components/schemas/ProvidersResponse' example: providers: - first_name: John last_name: Smith organization_name: null display_name: John Smith, MD credential: MD entity_type: individual npi: '1972113918' specialty: Family Medicine specialization: General Practice address_line_1: 123 Main St city: Phoenix state: AZ zip_code: '85001' meta: page_number: 1 page_size: 50 result_count: 1 '400': $ref: '#/components/responses/BadRequest' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '502': $ref: '#/components/responses/BadGateway' '503': $ref: '#/components/responses/ServiceUnavailable' '504': $ref: '#/components/responses/GatewayTimeout' components: responses: NotFound: description: The requested resource could not be found. Emitted by the backend when no supported data matches the request (for example, no counties for a ZIP), and by the API edge for unknown paths. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: not_found message: No counties found for ZIP 12345. Forbidden: description: The API key is missing, invalid, not authorized for this API, or the request was blocked at the API edge. Public API requests may receive this edge-generated response before the backend can return a runtime 401. When a request reaches the backend, 403 may also mean the developer account or integration is not in the state required for that operation. For `POST /v1/enrollment-sessions`, that operation documents its own flow-scoped 403 requirements (both flows require approved on-exchange enrollment access, plus `agent_assisted` OAuth link vs `self_service` deeplink agent ID) instead of using this shared response reference. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: forbidden message: The supplied API key is missing, invalid, or not authorized for this API. InternalError: description: An unexpected error occurred. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: internal_error message: An unexpected error occurred. ServiceUnavailable: description: The API is temporarily unable to serve the request. This may be returned by the API edge when the backend is unhealthy, or by the backend when an upstream catalog, quoting, or lookup service is unavailable. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: service_unavailable message: The service is temporarily unavailable. Please try again later. BadGateway: description: The API edge received an invalid response from the backend. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: bad_gateway message: The API edge received an invalid response from the backend. Please try again later. BadRequest: description: 'Request validation failed. The response body''s `error.details` map contains per-field messages when the backend produced the error; edge-generated 400s omit `details`. A malformed `Idempotency-Key` header is also rejected here with `code: idempotency_key_invalid`.' content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: invalid_request message: Validation failed. details: zip_code: - ZIP code must be 5 digits RateLimited: description: Rate limit exceeded. Clients should back off and retry after the delay advertised in the Retry-After header. headers: Retry-After: description: Number of seconds the client should wait before retrying. schema: type: integer content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: rate_limited message: Request rate limit exceeded. Please slow down and retry shortly. GatewayTimeout: description: The API edge did not receive a timely response from the backend. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: error: code: gateway_timeout message: The API edge did not receive a timely response from the backend. Please try again later. schemas: ProvidersResponse: type: object additionalProperties: true required: - providers - meta properties: providers: type: array description: Provider results for the current page. Empty when there are no matches. items: $ref: '#/components/schemas/ProviderEntity' meta: $ref: '#/components/schemas/ProvidersResponseMeta' CountiesResponse: type: object additionalProperties: true required: - counties properties: counties: type: array items: $ref: '#/components/schemas/CountySummary' IssuersResponse: type: object additionalProperties: true required: - issuers properties: issuers: type: array description: Issuers active in the requested state, sorted by `name` (case-insensitive) and then by `hios_issuer_id` as a stable tiebreaker. One entry per HIOS issuer. items: $ref: '#/components/schemas/IssuerSummary' CountySummary: type: object additionalProperties: true required: - fips_code - name - state properties: fips_code: type: string description: Five-digit county FIPS code. The first two digits identify the county's state. name: type: string state: type: string description: Two-letter state code for this county, derived from the FIPS code. Border ZIP responses can include counties from more than one state. ErrorResponse: type: object description: 'Canonical error envelope returned by HealthSherpa-owned API endpoint, infrastructure filter, and backend service failures. The direct enrollment endpoints return their own service failures using `EnrollmentErrorsResponse` (`errors[]`), but edge-generated failures on those endpoints (API Gateway and WAF, e.g. a missing or invalid API key) still use this `ErrorResponse` envelope. Clients should treat `error.code` as the stable programmatic identifier and surface `error.message` to humans. Recognized codes include: - `unauthorized` (401) - backend runtime authentication failure when a request reaches a runtime that performs its own authentication - `forbidden` (403) - missing/invalid/unauthorized API key at the API edge, or edge policy deny - `invalid_request` (400) - request body or parameter validation failed - `not_found` (404) - resource or route not found - `payload_too_large` (413) - request body exceeded edge limits - `unsupported_media_type` (415) - unsupported Content-Type - `rate_limited` (429) - edge rate-limiting - `service_unavailable` (503) - the edge is failing to reach the backend, or an upstream catalog/quoting/lookup service is unavailable - `bad_gateway` (502) - the edge received an invalid response from the backend - `gateway_timeout` (504) - the edge did not receive a timely response from the backend - `internal_error` (500) - unexpected server error' additionalProperties: true required: - error properties: error: type: object additionalProperties: true required: - code - message properties: code: type: string description: Stable, machine-readable identifier for the error class (see ErrorResponse description for recognized values). Clients should branch on this field rather than on `message` or HTTP status alone. message: type: string description: Human-readable explanation of the error, suitable for logging or surfacing to an end user. details: type: object description: Optional per-field validation details. Keys are dotted/indexed paths (e.g. `household.applicants[0].age`) and values are arrays of full-sentence error messages. additionalProperties: type: array items: type: string IssuerSummary: type: object additionalProperties: true required: - name - hios_issuer_id properties: name: type: string description: Issuer display name. hios_issuer_id: type: string pattern: ^[0-9]{5}$ description: Five-digit HIOS issuer ID. ProviderEntity: type: object additionalProperties: true required: - npi properties: first_name: type: - string - 'null' description: Individual provider's first name. Null for organizations. last_name: type: - string - 'null' description: Individual provider's last name. Null for organizations. organization_name: type: - string - 'null' description: Organization marketing name. Null for individuals. display_name: type: - string - 'null' description: 'Human-friendly label: organization name for facilities, or "First Last, Credential" for individuals.' credential: type: - string - 'null' description: Professional credential (e.g. MD, DO, NP). entity_type: type: - string - 'null' description: Provider entity type, typically `individual` or `organization`. npi: type: string pattern: ^[0-9]{10}$ description: Ten-digit National Provider Identifier. Use this value (no suffix) when referencing the provider in enrollment requests. specialty: type: - string - 'null' description: Primary taxonomy classification (e.g. Family Medicine). specialization: type: - string - 'null' description: Primary taxonomy specialization (e.g. General Practice). address_line_1: type: - string - 'null' description: First line of the provider's street address (number and street). city: type: - string - 'null' state: type: - string - 'null' description: Two-letter state code. zip_code: type: - string - 'null' description: The provider's address ZIP code (not the search ZIP). ProvidersResponseMeta: type: object additionalProperties: true required: - page_number - page_size - result_count properties: page_number: type: integer description: Current page number. page_size: type: integer description: Results per page (at most 50). result_count: type: integer description: Total number of matches across all pages. securitySchemes: ApiKeyAuth: type: apiKey in: header name: x-api-key