> ## 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.

# AdvancedMD Unapply Payment

> AdvancedMD payment unapply tool exposed through the Agents Platform.

This document covers `advancedmd_unapply_payment`, the AdvancedMD payment unapply tool exposed through the Agents Platform. It detaches an applied payment from the charge it was applied to, leaving it as an unapplied credit on the patient account. Optionally (`move_balance_to_insurance=true`) it then moves the charge's remaining patient responsibility to the insurance balance. For locating the payment row and its charge first, see [AdvancedMD Find Charge](/docs/tools/advancedmd-find-charge); for posting payments, see [AdvancedMD Post Payment](/docs/tools/advancedmd-post-payment).

## Authentication

AdvancedMD payment unapply uses the existing AdvancedMD connector:

* `office_code`
* `username`
* `password`

The tool uses the same audited AdvancedMD client as the other AdvancedMD tools. It does not accept raw AdvancedMD tokens or direct PM REST URLs — the client derives the tenant REST base from the login-returned URL and manages the security token itself (including one transparent re-authentication retry if AdvancedMD returns HTTP 401).

If AdvancedMD is not connected, the tool returns a structured `advancedmd_not_connected` result with suggested actions:

* Connect AdvancedMD and try again
* Stop task

## Safety

`advancedmd_unapply_payment` is a mutating billing tool. It calls the AdvancedMD PM REST endpoint `POST {rest}/transaction/Payments/{payment_id}/unapply`, which immediately detaches the payment from its charge.

`dry_run` defaults to `false` (the tool unapplies immediately, matching `advancedmd_create_claim`). Set `dry_run=true` to preview the exact endpoint call without executing it.

The unapply call is a non-idempotent financial write: the client never replays it on HTTP 429 or transient 5xx errors. The only automatic retry is a single re-authentication replay on HTTP 401, which is safe because AdvancedMD rejects unauthenticated requests before processing them.

The optional balance move is a second financial write (`updvisitwithcharges`), shared with [AdvancedMD Transfer Balance](/docs/tools/advancedmd-transfer-balance). It reads the charge's **live** balances from AdvancedMD *after* the unapply commits, then moves the patient balance and portion together onto insurance, setting the patient side to `0.00`. The write echoes the charge's FID references and line fields (`proccode`/`diagcodes`/`modcodes`/`respparty`, `fee`/`allowed`/etc.) so AdvancedMD's `CD_FIDReferences` constraint is satisfied, and it refuses any move that would drive a balance negative (`CD_Balances`). It skips the write entirely when the charge has no patient responsibility to move.

## Provider profile & facility preservation

`updvisitwithcharges` rewrites the **visit** record as well as the charge, and clears any attribute it is not given. Per the AdvancedMD API docs (June 2025, *UpdateVisitWithNewCharges*) the billing provider and facility are `@profile` and `@facility` **on the `<visit>` element** — not on the charge. Our payload sent `visit` with only its `@id`, so every successful write reset the visit's provider and facility to AdvancedMD's blank records (`prof1` / `fac1`), which surfaces in reports as "Billing Provider Profile: N/A" and "Facility: N/A - N/A". 58 charges were affected between 27–30 Jul 2026.

Verified live on charge 350485 (2026-07-31): the same attributes on the **charge** element were accepted and ignored; on the **visit** element they took effect and restored `provcode`/`faccode`, with no other field disturbed.

The write now:

1. **Reads the visit record** from `getchargedetaildataicd10` — `profile`, `facility`, `date`, `insorder`, `refplan` — the same call that already supplies the balances, so no extra round trip.
2. **Echoes them on the visit element** as bare ids (`prof179` → `@profile="179"`), matching the docs' write sample.
3. **Fails closed.** If the visit's provider cannot be read, the write is refused (`billing_identity_unresolved`) rather than run without it. No balance is moved. This is the real protection, and it is why a flat (non-nested) charge-detail response now blocks the write.
4. **Verifies after the write.** The visit's provider is re-read and compared: unchanged, changed/cleared (`provider_cleared: true` plus the codes to restore and a `⚠️ Billing Provider — CLEARED BY THIS WRITE` summary row), or the read-back itself failed (`provider_verification_failed: true`, reported as unknown and never as intact). The transfer is still reported as successful in all cases, because the money did move.

`ADVANCEDMD_CHARGE_WRITE_ENABLED` is an ops **kill switch** for every `updvisitwithcharges` write — this tool's balance move, [AdvancedMD Transfer Balance](/docs/tools/advancedmd-transfer-balance) and [AdvancedMD Add Codes To Visit](/docs/tools/advancedmd-add-codes-to-visit). Writes are enabled by default; set it to `false`/`0`/`no`/`off` to stop them without a deploy.

## Inputs

Required:

* `payment_id`: AdvancedMD payment detail ID of the applied payment to unapply (numeric, e.g. `9225081`). A leading `pay` prefix is stripped automatically; any other non-numeric value is rejected.

Optional:

* `dry_run`: Defaults to `false`. When `true`, returns the unapply call that would be made without executing it.
* `move_balance_to_insurance`: Defaults to **`true`** — after the unapply succeeds, the charge's remaining patient balance and patient portion are moved to the insurance balance/portion. Set to `false` to only unapply.
* `charge_id`: AdvancedMD charge ID the payment was applied to (from `advancedmd_find_charge`'s `ehr_charge_id`). Required unless `move_balance_to_insurance=false` (the tool errors with guidance when missing).
* `patient_id` / `visit_id`: Only used for the balance move, and only needed when AdvancedMD's charge detail response doesn't identify the patient/visit itself (the tool errors asking for them if so). `advancedmd_find_charge` returns both (`ehr_patient_id`, `ehr_visit_id`).

Note that `payment_id` is the **payment detail ID** of the applied payment row (the per-charge application record), not the parent payment ID. Both appear in the output: the detail ID is what you pass in, and `paymentId` in the response is the parent payment it belongs to.

## Recommended Workflow

### Standard Unapply Flow

1. Use `advancedmd_find_charge` to locate the charge — its `match.applied_payments` lists every payment applied to the charge, and each entry's `payment_id` is what this tool takes (`ehr_charge_id` is the `charge_id`). Confirm the amount and check number match the payment that should be unapplied, and skip entries with `void != "0"`.
2. Call `advancedmd_unapply_payment` with `dry_run=true` and review the endpoint preview.
3. Call again with `dry_run=false` (or omit `dry_run`) to unapply.
4. Confirm `unapplied` is `true` and the returned payment details (amount, check number, carrier) match expectations.

### Unapply + Move Balance to Insurance Flow (default)

This is the default behavior: after unapplying, the charge's entire patient responsibility moves to insurance (e.g. the payment is being clawed back and the claim goes back to the carrier).

1. Use `advancedmd_find_charge` to get the charge (`ehr_charge_id`, plus `ehr_patient_id`/`ehr_visit_id`) and the applied payment's `payment_id`.
2. Call `advancedmd_unapply_payment` with `payment_id` and `charge_id`.
3. The tool unapplies first, then re-reads the charge's live balances (which now include the unapplied amount back on the charge) and moves patient balance/portion to insurance.
4. Confirm `balance_transfer.transferred` in the output and the before/after amounts in the summary table.

A payment that spans multiple charges has one payment detail row per charge — run this flow once per (payment detail, charge) pair.

**Partial failure:** if the unapply succeeds but the balance move fails, the result is still a success (`unapplied: true`) with `balance_transfer.status = "failed"` — an error result would wrongly suggest nothing happened. Whether to re-run depends on `balance_transfer.retryable`:

* **`retryable: true`** — re-run the tool with the same arguments. The already-done unapply comes back from AdvancedMD as either a 409 "payment is not applied" or a 400 "Payment cannot be unapplied", both of which the tool resolves to `already_unapplied: true`, and it then retries only the balance move.
* **`retryable: false`** (`reason: "payment_detail_fid_constraint"`) — AdvancedMD rejected the move with its `PD_FIDReferences` constraint on `actv_PaymentDetails`. **This was caused by the same visit-element omission described above** and is fixed: when the write omitted the visit's `@profile`, the payment-detail row AdvancedMD derives had no valid provider to reference, so its own CHECK constraint rejected it. Confirmed on charges 301828 and 257186, both stuck on this constraint since 30 Jul 2026 and both transferred cleanly once the visit's provider was echoed. The classification is kept as a defensive net: if the constraint ever fires again it is a property of the charge, so the tool marks it non-retryable and asks for a manual transfer instead of looping.

With `move_balance_to_insurance=false` there is nothing to chain, so both rejections stay plain errors.

### Repost / Correction Flow

Use this when a payment was applied to the wrong charge or with the wrong amounts.

1. Unapply the payment with `advancedmd_unapply_payment`.
2. The payment becomes an unapplied credit on the patient account.
3. Re-apply it to the correct charge with [AdvancedMD Post Payment](/docs/tools/advancedmd-post-payment), passing the correct `charge_id` and amounts.

### Missing AdvancedMD Connector Flow

Use this when the tool reports `advancedmd_not_connected`.

1. Do not retry with the same inputs immediately.
2. Ask the user to either connect AdvancedMD and try again, or stop the task.
3. After AdvancedMD is connected, call the tool again.

### Dry-Run Example

```json theme={null}
{
  "dry_run": true,
  "payment_id": "9225081",
  "charge_id": "8422113"
}
```

### Unapply + Move Balance Example (default)

```json theme={null}
{
  "payment_id": "9225081",
  "charge_id": "8422113"
}
```

### Unapply-Only Example

```json theme={null}
{
  "payment_id": "9225081",
  "move_balance_to_insurance": false
}
```

## Error Handling

The tool returns a structured tool error for invalid inputs (missing or non-numeric `payment_id`).

If AdvancedMD returns a non-2xx status, the tool surfaces the HTTP status and AdvancedMD's response body as a tool error. It does not retry. Two rejections are handled specially, and only when the balance move is enabled:

* **409 "payment is not applied"** — unambiguous, so the tool treats the unapply as already done and proceeds to the balance move.
* **400 "Payment cannot be unapplied"** — AdvancedMD's description lumps four states together ("transferred, protected, voided, or unapplied payment detail"), and only the last means the work is done. The tool therefore re-reads the charge's applied payments and branches on what it finds:
  * payment no longer applied → treated as already unapplied (`unapply_state: "already_unapplied"`), balance move proceeds;
  * payment still applied → it is transferred, protected, or voided and AdvancedMD will not release it through the API. This is a tool error and **no balance is moved**; resolve the payment in AdvancedMD or pass the correct payment detail id;
  * state unresolvable (no `patient_id`, or the charge is beyond the scanned history pages) → `unapply_state: "already_unapplied_unverified"`. Nothing was unapplied by the call, the requested balance move still runs, and the summary says the state is unconfirmed.

Passing `patient_id` alongside `charge_id` is what enables that read-back, so include both.

A 2xx response with an unexpected body is still reported as a successful unapply — the structured output then carries the raw response under `amd_response` instead of the parsed `payment` object.

## Output

The structured output includes:

* `unapplied`: `true` only when `dry_run=false` and AdvancedMD accepted the unapply.
* `dry_run`: whether the call was a dry run.
* `payment_id`: the normalized payment detail ID that was targeted.
* `payment`: the payment detail returned by AdvancedMD after unapplying — `paymentDetailId`, `paymentId`, `paymentAmount`, `checkNumber`, `carrierCode`, `paymentCode`, `profileCode`, `depositDate`, `note`, and related fields.
* `endpoint` / `method`: on dry runs, the call that would be made.
* `already_unapplied`: `true` when the unapply step was skipped because AdvancedMD reported the payment as not currently applied (only when the balance move is enabled).
* `unapply_state`: `"already_unapplied"` or `"already_unapplied_unverified"` — set only when the unapply step was skipped, distinguishing a confirmed-detached payment from one whose state could not be read back.
* `balance_transfer` (absent with `move_balance_to_insurance=false`): the transfer outcome — `transferred`, `charge_id`, `direction`, `amount`, the previous patient/insurance balance and portion, and the new patient/insurance balance and portion; or `{status: "failed", error, retryable}` when the move failed after a committed unapply (plus `reason` and `suggested_actions` when `retryable` is `false`); or `{status: "planned"}` on dry runs.

The markdown summary renders an "Unapplied" status table with the payment detail ID, parent payment ID, amount (normalized to two decimals), payment code, carrier, check number, profile, deposit date, and note, followed by a balance-transfer table (before → after amounts) when the move ran.
