> ## Documentation Index
> Fetch the complete documentation index at: https://docs.haau3.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDKs

> Open-source clients for TypeScript, Python and Go: every endpoint as a typed call, with retries, typed errors, paging and batches.

<Info>
  The haau3 platform API is in preview: it may change as we build, usually to add features or improve performance.
</Info>

The platform API has an open-source SDK for TypeScript, Python and Go. Each is generated from the
API's [OpenAPI document](https://api.haau3.com/openapi.json), so every endpoint is a method named by
its operationId, with its parameters and its answer typed. Each also retries after a timeout, a
network failure, a `429` or a `5xx`, gives typed errors that never print a response body, follows a
FHIR search's pages, and sends several requests as one batch.

| | Package | Requires | Source |
| - | - | - | - |
| TypeScript | [`@haau3/sdk`](https://www.npmjs.com/package/@haau3/sdk) | Node 22 or newer | [haau3/haau3-node](https://github.com/haau3/haau3-node) |
| Python | [`haau3`](https://pypi.org/project/haau3/) | Python 3.10 or newer | [haau3/haau3-python](https://github.com/haau3/haau3-python) |
| Go | [`github.com/haau3/haau3-go`](https://pkg.go.dev/github.com/haau3/haau3-go) | Go 1.26 or newer | [haau3/haau3-go](https://github.com/haau3/haau3-go) |

All three are MIT licensed. Each runs on a server only, and refuses to start in a browser, where an
API key would be public. Every operation's page in the [API reference](/api-reference) shows its
call in each language beside the curl command.

## Install

<CodeGroup>
  ```sh TypeScript theme={null}
  npm install @haau3/sdk
  ```

  ```sh Python theme={null}
  pip install haau3
  ```

  ```sh Go theme={null}
  go get github.com/haau3/haau3-go
  ```
</CodeGroup>

## Authenticate

Create an API key on [platform.haau3.com](https://platform.haau3.com) (see
[Authentication](/authentication)). Each SDK reads it from `HAAU3_API_KEY`, or takes it as an
option. Make one client and reuse it: it holds a pool of connections.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Haau3 } from "@haau3/sdk";

  const haau3 = new Haau3(); // reads HAAU3_API_KEY
  ```

  ```python Python theme={null}
  from haau3 import Haau3

  client = Haau3()  # reads HAAU3_API_KEY
  ```

  ```go Go theme={null}
  client, err := haau3.NewClient() // reads HAAU3_API_KEY
  if err != nil {
  	return err
  }
  ```
</CodeGroup>

Python also has `AsyncHaau3`, with the same methods awaited, on asyncio and on trio.

## A first call

<CodeGroup>
  ```ts TypeScript theme={null}
  const plan = await haau3.readInsurancePlan("H2406-013-000");
  console.log(plan.name); // "AARP Medicare Advantage from UHC FL-0021 (PPO)"
  ```

  ```python Python theme={null}
  plan = client.read_insurance_plan("H2406-013-000")
  print(plan.get("name"))  # "AARP Medicare Advantage from UHC FL-0021 (PPO)"
  ```

  ```go Go theme={null}
  plan, err := client.ReadInsurancePlan(ctx, "H2406-013-000")
  if err != nil {
  	return err
  }
  if plan.Name != nil {
  	fmt.Println(*plan.Name) // "AARP Medicare Advantage from UHC FL-0021 (PPO)"
  }
  ```
</CodeGroup>

A FHIR answer is FHIR R4 in each language's own form: `@types/fhir`'s types in TypeScript (exported
as `fhir4`), plain dicts typed with TypedDicts from HL7's R4 schema in Python, and structs
generated from the same schema in Go's `fhir` package. FHIR elements are optional, so Python reads them with `.get()` and Go
holds each as a pointer. The other services' answers have the API's own types.

## One call per service

<CodeGroup>
  ```ts TypeScript theme={null}
  // Scheduling: open appointment slots, here in Boston.
  const { slots, total } = await haau3.searchSlots({ city: "Boston", state: "MA", limit: 10 });

  // Provider directory: a practitioner by NPI, from CMS's National Provider Directory.
  const practitioners = await haau3.searchPractitioner({ identifier: "1093209884" });

  // Terminology: a LOINC code's display and properties.
  const glucose = await haau3.codeSystemLookup({ system: "http://loinc.org", code: "2345-7" });

  // Questionnaires: a form by its id, such as the PHQ-9.
  const phq9 = await haau3.readQuestionnaire("44249-1");

  // Plan Benefits: a Medicare Advantage or ACA Marketplace plan, and which providers are in its network.
  const plan = await haau3.readInsurancePlan("H2406-013-000");
  const network = await haau3.checkInsurancePlanNetwork("H3359-038-000", { npi: ["1518929058"] });

  // MedList: one reconciled medication list from one patient's FHIR medication records. It receives
  // patient data, so send it only under an executed agreement.
  const list = await haau3.reconcileMedications(records);
  ```

  ```python Python theme={null}
  # Scheduling: open appointment slots, here in Boston.
  found = client.search_slots(city="Boston", state="MA", limit=10)
  print(found["total"], len(found["slots"]))

  # Provider directory: a practitioner by NPI, from CMS's National Provider Directory.
  practitioners = client.search_practitioner(identifier="1093209884")

  # Terminology: a LOINC code's display and properties.
  glucose = client.code_system_lookup(system="http://loinc.org", code="2345-7")

  # Questionnaires: a form by its id, such as the PHQ-9.
  phq9 = client.read_questionnaire("44249-1")

  # Plan Benefits: a Medicare Advantage or ACA Marketplace plan, and which providers are in its network.
  plan = client.read_insurance_plan("H2406-013-000")
  network = client.check_insurance_plan_network("H3359-038-000", npi=["1518929058"])

  # MedList: one reconciled medication list from one patient's FHIR medication records. It receives
  # patient data, so send it only under an executed agreement.
  reconciled = client.reconcile_medications(records)
  ```

  ```go Go theme={null}
  // Scheduling: open appointment slots, here in Boston.
  found, err := client.SearchSlots(ctx, haau3.SearchSlotsParams{City: new("Boston"), State: new("MA"), Limit: new(10)})

  // Provider directory: a practitioner by NPI, from CMS's National Provider Directory.
  practitioners, err := client.SearchPractitioner(ctx, haau3.SearchPractitionerParams{Identifier: new("1093209884")})

  // Terminology: a LOINC code's display and properties.
  glucose, err := client.CodeSystemLookup(ctx, haau3.CodeSystemLookupParams{System: new("http://loinc.org"), Code: "2345-7"})

  // Questionnaires: a form by its id, such as the PHQ-9.
  phq9, err := client.ReadQuestionnaire(ctx, "44249-1")

  // Plan Benefits: a Medicare Advantage or ACA Marketplace plan, and which providers are in its network.
  plan, err := client.ReadInsurancePlan(ctx, "H2406-013-000")
  network, err := client.CheckInsurancePlanNetwork(ctx, "H3359-038-000", haau3.CheckInsurancePlanNetworkParams{NPI: []string{"1518929058"}})

  // MedList: one reconciled medication list from one patient's FHIR medication records. It receives
  // patient data, so send it only under an executed agreement.
  reconciled, err := client.ReconcileMedications(ctx, records)
  ```
</CodeGroup>

Each SDK's `api.md` lists every method by service, with its request and its page in the API
reference: [TypeScript](https://github.com/haau3/haau3-node/blob/main/api.md),
[Python](https://github.com/haau3/haau3-python/blob/main/api.md) and
[Go](https://github.com/haau3/haau3-go/blob/main/api.md).

## Errors

<CodeGroup>
  ```ts TypeScript theme={null}
  import { AuthError, NoAnswerError, RefusedError } from "@haau3/sdk";

  try {
    await haau3.readInsurancePlan(id);
  } catch (error) {
    if (error instanceof RefusedError) {
      error.status; // 400, 404, 410 and so on
      error.outcome; // the OperationOutcome; Scheduling's errors carry `problem`
    } else if (error instanceof NoAnswerError) {
      error.reason; // "timeout", "network", "rate-limited", "unavailable", "server-error" or "unreadable"
      error.retryAfter; // seconds, when the API said
      error.detail; // for a timeout or a network failure: what failed, such as "ECONNREFUSED" or "TimeoutError"
    } else if (error instanceof AuthError) {
      // the key was missing, unknown or revoked
    }
  }
  ```

  ```python Python theme={null}
  from haau3 import AuthError, NoAnswerError, RefusedError

  try:
      client.read_insurance_plan(plan_id)
  except RefusedError as error:
      print(error.status)  # 400, 404, 410 and so on
      print(error.outcome)  # the OperationOutcome; Scheduling's errors carry `problem`
  except NoAnswerError as error:
      print(error.reason)  # "timeout", "network", "rate-limited", "unavailable", "server-error" or "unreadable"
      print(error.retry_after)  # seconds, when the API said
      print(error.detail)  # for a timeout or a network failure: what failed, such as "ConnectError: Connection refused"
  except AuthError:
      pass  # the key was missing, unknown or revoked
  ```

  ```go Go theme={null}
  plan, err := client.ReadInsurancePlan(ctx, planID)
  var e *haau3.Error
  if errors.As(err, &e) {
  	switch e.Kind {
  	case haau3.KindRefused:
  		fmt.Println(e.Status)    // 400, 404, 410 and so on
  		fmt.Println(e.Outcome()) // the OperationOutcome; Scheduling's errors carry e.Problem()
  	case haau3.KindNoAnswer:
  		fmt.Println(e.Reason)     // timeout, network, rate-limited, unavailable, server-error or unreadable
  		fmt.Println(e.RetryAfter) // when the API said
  		fmt.Println(e.Detail)     // for a timeout or a network failure: what failed, such as "dial: connection refused"
  	case haau3.KindAuth:
  		// the key was missing, unknown or revoked
  	case haau3.KindNotSent:
  		// the SDK refused to send it: a required parameter left empty, a URL on another host
  	}
  }
  ```
</CodeGroup>

Every error the API answered carries its request id. No answer is never reported as an empty
result, and an empty result is never an error. An error's message never includes the body the API
answered with, and the body is never printed when the error is logged. A FHIR answer is checked
against the resource types its operation answers with, and anything else is a no-answer error with
the reason `unreadable`. [Errors](/errors) lists every status the API answers.

## Request ids and timing

<CodeGroup>
  ```ts TypeScript theme={null}
  const { data: plan, requestId, durationMs, attempts } = await haau3.readInsurancePlan(id).withResponse();
  ```

  ```python Python theme={null}
  response = client.with_response.read_insurance_plan(plan_id)
  print(response.data.get("name"), response.request_id, response.duration_ms, response.attempts)
  ```

  ```go Go theme={null}
  var info haau3.ResponseInfo
  plan, err := client.ReadInsurancePlan(ctx, planID, haau3.WithResponseInfo(&info))
  fmt.Println(info.RequestID, info.Duration, info.Attempts)
  ```
</CodeGroup>

Quote the request id when you ask for help: it finds the request in the API's own logs. To find a
request by an id of your own, pass a UUID as the call's correlation id, and it comes back on the
answer and on every error the API answered.

## Paging

Each SDK follows a FHIR search's `next` links on the client's own host, one page at a time, or one
resource at a time across every page:

<CodeGroup>
  ```ts TypeScript theme={null}
  const first = await haau3.searchInsurancePlan({ "coverage-area-state": "NC" });
  for await (const page of haau3.pages(first)) {
    for (const entry of page.entry ?? []) console.log(entry.resource?.id);
  }
  ```

  ```python Python theme={null}
  first = client.search_insurance_plan(coverage_area_state="NC")
  for page in client.pages(first):
      for entry in page.get("entry", []):
          resource = entry.get("resource")
          if resource is not None:
              print(resource.get("id"))
  ```

  ```go Go theme={null}
  first, err := client.SearchInsurancePlan(ctx, haau3.SearchInsurancePlanParams{CoverageAreaState: new("NC")})
  if err != nil {
  	return err
  }
  for page, err := range client.Pages(ctx, first) {
  	if err != nil {
  		return err
  	}
  	for _, entry := range page.Entry {
  		if plan, ok := entry.Resource.(*fhir.InsurancePlan); ok {
  			fmt.Println(*plan.ID)
  		}
  	}
  }
  ```
</CodeGroup>

<CodeGroup>
  ```ts TypeScript theme={null}
  for await (const resource of haau3.resources(first)) {
    if (resource.resourceType === "InsurancePlan") console.log(resource.name);
  }
  ```

  ```python Python theme={null}
  for resource in client.resources(first):
      if resource["resourceType"] == "InsurancePlan":
          print(resource.get("name"))
  ```

  ```go Go theme={null}
  for resource, err := range client.Resources(ctx, first) {
  	if err != nil {
  		return err
  	}
  	fmt.Println(resource.ResourceType())
  }
  ```
</CodeGroup>

Scheduling's searches page with `limit` and `offset` instead, and answer with a `total`.

## Several requests in one

A batch sends several operations of the FHIR base as one request, and each gets its own result, in
the order given:

<CodeGroup>
  ```ts TypeScript theme={null}
  const [plan, counties] = await haau3.batch((b) => [
    b.readInsurancePlan("H2406-013-000"),
    b.listInsurancePlanCounties({ state: "NC" }),
  ]);

  if (plan.ok) {
    plan.data; // fhir4.InsurancePlan
  } else {
    plan.error; // a RefusedError or a NoAnswerError, as readInsurancePlan alone would have thrown
  }
  ```

  ```python Python theme={null}
  from haau3 import BatchOk, calls

  plan_result, counties_result = client.batch(
      calls.read_insurance_plan("H2406-013-000"),
      calls.list_insurance_plan_counties(state="NC"),
  )

  if isinstance(plan_result, BatchOk):
      print(plan_result.data.get("name"))  # an InsurancePlan
  else:
      print(plan_result.error)  # a RefusedError or a NoAnswerError, as read_insurance_plan alone would have raised
  ```

  ```go Go theme={null}
  planCall := haau3.Calls.ReadInsurancePlan("H2406-013-000")
  counties := haau3.Calls.ListInsurancePlanCounties(haau3.ListInsurancePlanCountiesParams{State: "NC"})

  if err := client.Batch(ctx, []haau3.Entry{planCall, counties}); err != nil {
  	return err // the batch as a whole failed
  }
  p, err := planCall.Result() // an *fhir.InsurancePlan, or the error ReadInsurancePlan alone would have returned
  ```
</CodeGroup>

An entry that fails does so alone, while the others answer. The API takes up to 100 entries in up
to 1 MiB, and gives a batch 60 seconds. Every operation on the FHIR base can go in a batch except
the batch itself and MedList's `$reconcile-medications`.

## Any request

Each SDK sends a request as you give it and returns what the API answered, whatever the status:

<CodeGroup>
  ```ts TypeScript theme={null}
  const { status, statusText, body, requestId } = await haau3.request({ method: "GET", path: "/v1/scheduling/publishers" });
  ```

  ```python Python theme={null}
  raw = client.request("GET", "/v1/scheduling/publishers")
  print(raw.status, raw.reason_phrase, raw.request_id)
  ```

  ```go Go theme={null}
  raw, err := client.Request(ctx, haau3.RawRequest{Method: "GET", Path: "/v1/scheduling/publishers"})
  if err != nil {
  	return err
  }
  fmt.Println(raw.Status, raw.StatusText, raw.RequestID, len(raw.Body()))
  ```
</CodeGroup>

## Options

Every option has a default:

| | TypeScript | Python | Go | Default |
| - | - | - | - | - |
| API key | `apiKey` | `api_key` | `WithAPIKey` | `HAAU3_API_KEY` |
| Where the API is | `baseURL` | `base_url` | `WithBaseURL` | `HAAU3_BASE_URL`, then `https://api.haau3.com` |
| Time for one attempt | `timeout` | `timeout` | `WithTimeout` | 65 seconds |
| Retries | `maxRetries` | `max_retries` | `WithMaxRetries` | 1 |

A call can set its own time and retries too. A retry waits a short, growing interval, or what the
API's `Retry-After` header says when that is 60 seconds or less. A longer one fails at once, carrying it. The API key is sent only to the base URL's host. Set
`HAAU3_BASE_URL` to run code you have not changed against a stand-in API of your own.

## Frameworks and testing

Each README covers its own language's frameworks and tests:

* TypeScript: [any runtime's `fetch` options](https://github.com/haau3/haau3-node#options), such as
  a proxy in Node or Next.js's caching.
* Python: [FastAPI, Django and Flask](https://github.com/haau3/haau3-python#using-it-with-fastapi-django-and-flask),
  and [testing your code](https://github.com/haau3/haau3-python#testing-your-code).
* Go: [net/http and Gin](https://github.com/haau3/haau3-go#using-it-with-nethttp-and-gin), and
  [testing your code](https://github.com/haau3/haau3-go#testing-your-code).

## Versions

Each SDK is `0.y.z` while the API is in preview: a minor release may change what a method takes or
answers, and a patch release never does. Each repository's `CHANGELOG.md` lists every release, and
a release that takes a new version of the API's spec names that version.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.