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

# PracticeSuite Save Patient

> Writes a PracticeSuite patient record and defines the canonical patient natural-key algorithm.

# PracticeSuite Save Patient

This document covers `practicesuite_save_patient`, the native PracticeSuite patient write tool exposed through the Agents Platform. It is also the home of the **canonical patient natural-key algorithm** shared by every PracticeSuite tool.

## Authentication

All PracticeSuite tools use the PracticeSuite connector. Connect it under Integrations with:

* `client_id` — the interface client ID issued by PracticeSuite for your organisation.
* `client_secret` — its paired secret.
* `environment` — `staging` or `production`. Defaults to `staging` if unset; production writes to live patient records.

Connecting runs the OAuth 2.0 client-credentials grant against `/v2/oauth/token`, which both proves the credentials work and seeds the access token and its expiry. The platform refreshes the token from then on; the tools never mint one except as a single reactive retry if a call comes back with `invalid_token`.

The tools are **off by default** — enable them per agent. If the connector is missing, every tool returns a non-error `practicesuite_not_connected` payload so the agent can ask the user to connect rather than failing the task.

## Account ID

Every PracticeSuite tool takes `account_id`. It is an **ordinary per-call argument, not a credential**: one client ID addresses many practice tenants, and `account_id` selects which one receives the write. Agents resolve it per document from their own lookup table. A wrong value writes a real patient into the wrong practice, so validate it upstream.

Because that value is model-visible and often derived from an extracted document, the connector supports an optional guard: set **Allowed Account IDs** on the integration to a comma-separated list, and any tool call naming an account outside it is rejected before a request is issued. Matching ignores case and surrounding spaces. Leave the field blank to allow every account the credential can reach — the behaviour of the export scripts this connector replaces. Setting it is recommended for any workspace writing to production.

## The patient natural key (`patient_pcref`)

PracticeSuite has **two distinct patient identifiers**, and they are not interchangeable:

| Identifier      | What it is                                                                                                                             | Used by                                                   |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `patient_pcref` | A natural key the **tool derives** from the claimant's name and date of birth. PracticeSuite stores it in the patient's `PCREF` field. | `practicesuite_save_patient`, `practicesuite_get_patient` |
| `patient_id`    | PracticeSuite's **internal UUID**, e.g. `196CCD0A90FB2053E0630100007FD249`. Returned by `practicesuite_get_patient`.                   | `practicesuite_upload_document`                           |

Passing one where the other is expected silently targets the wrong chart. The tools keep them as separately named parameters for exactly that reason.

**The connector derives this for you.** Pass `first_name`, `last_name` and `dob` and the tool computes the key; `patient_pcref` is an optional override, needed only to target a record created under a different key (for example one written by the legacy Python pipeline). All three tools that take the key derive it through the same function, so they cannot drift apart.

**Your job — splitting the name.** The tools take `first_name` and `last_name` as separate fields and never parse a combined name. Extraction owns that split, including the two common source formats: `Last, First` (split on the comma) and `First Last` (first token is the given name, the rest is the surname). Get this wrong and you produce a different key for the same claimant, which is the one part of the flow the connector cannot protect you from.

**The tool's job — everything after that**, for reference:

1. Concatenate `first_name + last_name`.
2. Remove all whitespace.
3. Truncate to **57 characters** if longer.
4. Append the date of birth as `MMDDYYYY` — zero-padded, always exactly 8 digits (`4/13/1989` and `04/13/1989` both → `04131989`).
5. Lowercase, and remove apostrophes.

So `first_name: "Tyler"`, `last_name: "Heath"`, `dob: "04/13/1989"` → `tylerheath04131989`.

**The key is not collision-proof.** The names are concatenated with no separator, so two different splits sharing a date of birth collide — `Anna` / `Bell` and `Ann` / `Abell` both derive `annabell…`. Because `savePatient` upserts on this key, a collision **updates the other patient's record** rather than erroring. This is inherent to the algorithm the legacy pipeline established, and changing it would strand every record already written under the old keys, so it stays. Two claimants colliding on both name letters and exact DOB is rare, but it is the one failure mode of this design worth knowing about.

**Matching is case-sensitive.** PracticeSuite returns "no patient associated with this ID" for an otherwise-identical key that differs only in case (verified). Derived keys are always lowercased, and an override is lowercased for you.

`dob` is accepted as `MM/DD/YYYY`, `M/D/YYYY`, `YYYY-MM-DD` or `MMDDYYYY`, and is always normalised to 8 zero-padded digits. Dash-separated month-first dates (`04-13-1989`) are **rejected**, not guessed: `DD-MM-YYYY` and `MM-DD-YYYY` are indistinguishable when both parts are 12 or less, and picking one silently would produce a different key for the same claimant. An override is still validated: no whitespace, and 65 characters at most.

## `practicesuite_save_patient`

Creates or updates a patient via `POST /v2/api/patient/savePatientAPI`. **This is a mutating tool.**

### Inputs

Required:

* `account_id` — practice tenant to write into.
* `first_name`, `last_name`, `dob` — the tool derives `patient_pcref` from these

Optional:

* `patient_pcref` — override the derived key (rarely needed)
* `middle_name`, `gender`, `cell_phone`, `email`
* `address_1`, `address_2`, `city`, `state`, `zip`
* `provider_code`, `provider_first_name`, `provider_last_name` — the main provider
* `ins_code`, `ins_name`, `policy_number` — become the single primary insurance entry
  (`ins_rank: primary`, `patient_relation_to_insured: SEL`); `policy_number` is the form's
  contract number, left blank when absent

`access_token`, `client_id`, `client_secret` and `environment` are injected from the integration and hidden from the model.

### Output

```json theme={null}
{
  "success": true,
  "response_code": "",
  "response_msg": "Demographics saved successfully"
}
```

PracticeSuite reports failure in the **response body, not the HTTP status**. A `success: false` (which also arrives on the wire as the string `"False"`) becomes a tool error carrying `response_code` and `response_msg`. Verified failure codes: `1013` (invalid JSON request format) and, from `getPatient`, `1020` (no patient for that ID).

### Limits and caveats

* **Repeat calls upsert — verified against the sandbox.** Saving the same `patient_pcref` twice updates the existing record and returns the same internal UUID and MRN rather than creating a duplicate, so a step retry is safe.
* **The full envelope is mandatory.** `savePatientAPI` validates against its complete schema and answers `1013` to any partial body — including a bare `{}`. The tool exposes only the fields above and the client fills every remaining field as an empty string; adding a new input means adding it to that envelope, not passing it through.
* The `account_id` is enforced server-side: a credential set that cannot reach an account gets HTTP 400 `"Not authorized to access this account."` rather than a silent write elsewhere.

## Agent Instruction Guidance

Pass the claimant's `first_name`, `last_name` and `dob` and let the tool compute the key — do not build it yourself, and never pass a `patient_id` returned by `practicesuite_get_patient` as the override. Resolve `account_id` from your practice lookup and confirm it before writing, since a wrong account writes into another practice entirely.
