---
title: "Match Suggestion (no-write)"
method: POST
path: "/v1/patients/match-suggestion"
tags: ["Patients"]
---

# Match Suggestion (no-write)

`POST /v1/patients/match-suggestion`

Run the patient matcher against caller-supplied identifiers and return the verdict. Never persists. The verdict's `kind` tells the caller how to render its picker: `hard` (lock — external-id / name+DOB uniqueness), `medium` (pre-fill with override — phone/email + name heuristic), `soft` (pre-fill, confirm — a single same-name patient with no date of birth to confirm), or `none`. Used by the file detail page to lock or pre-fill its patient picker, and by future eligibility / prior-auth extraction surfaces. Identifiers are normalized with the same forgiving parsers the upsert endpoint uses, so callers can pass raw values from extracted document data.

## Request body

- MatchSuggestionRequest — Identifiers the matcher will resolve to a patient. Forgiving on the wire — same `parse_*_safe` normalizers the upsert endpoint runs, so a partly-formatted phone or a free-text DOB still matches. Pass raw values from extracted document data, form fields, or eligibility payloads — the service normalizes before calling the matcher. Empty/missing fields are skipped (matcher tiers gate on presence, so unsupplied identifiers just narrow the tier set).
  - `external_id` ExternalIdInput — External-id pair used for matching and (optionally) record creation.
    - `type_id` string, uuid, required — external_id_types.id (must belong to caller's company)
    - `value` string, required — External identifier value
  - `first_name` string, nullable
  - `last_name` string, nullable
  - `date_of_birth` string, nullable — DOB in any of: YYYY-MM-DD, YYYYMMDD, MM/DD/YYYY, MM-DD-YYYY, MM/DD/YY. Unparseable values are ignored.
  - `phone_number` string, nullable
  - `email` string, nullable

## Response `200`

Successful Response

- MatchSuggestionResponse — Matcher verdict + optional slim patient. Never includes side-effect data.
  - `kind` 'hard' | 'medium' | 'soft' | 'none', required — How the caller should render the picker: `hard` = lock to the matched patient (DB-enforced uniqueness leaves exactly one valid choice); `medium` = pre-fill with override (contact-channel heuristic); `soft` = pre-fill with override (name-only suggestion, confirm before use); `none` = free.
  - `reason` union — Which matcher tier resolved the patient (`external_id`, `demographics`, `phone_fuzzy_name`, `email_fuzzy_name`, `name_only`). Null when `kind` is `none`.
    - 'external_id' | 'demographics' | 'phone_fuzzy_name' | 'email_fuzzy_name'
    - 'name_only'
  - `patient` MatchSuggestionPatient — Slim patient surface for the suggestion response. The full `Patient` schema includes nested expansions (payors, referrals, custom_fields, external_id_values) that callers of the suggestion endpoint don't need — they're picking lock vs suggest vs free, not rendering the patient detail page. Keeping this narrow keeps the response payload small and the contract stable as `Patient` evolves.
    - `id` string, required
    - `first_name` string, nullable
    - `last_name` string, nullable
    - `phone_number` string, nullable
    - `email` string, nullable
    - `day_of_birth` string, date, nullable
    - `workflow_stage` WorkflowStageSummary — Lightweight workflow stage for patient expansion responses.
      - `id` string, uuid, required
      - `title` string, nullable
      - `actions` WorkflowStageAction[], nullable
      - `order` integer, nullable
      - `description` string, nullable
    - `active` boolean

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/getsolum/apis/solum-health-api.md) · [All operations](https://skmtc.net/getsolum/apis/solum-health-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/getsolum/solum-health-api/revisions/2bf886cdf729/schema)
