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; for posting payments, see AdvancedMD Post Payment.
Authentication
AdvancedMD payment unapply uses the existing AdvancedMD connector:office_codeusernamepassword
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. 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:
- Reads the visit record from
getchargedetaildataicd10—profile,facility,date,insorder,refplan— the same call that already supplies the balances, so no extra round trip. - Echoes them on the visit element as bare ids (
prof179→@profile="179"), matching the docs’ write sample. - 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. - Verifies after the write. The visit’s provider is re-read and compared: unchanged, changed/cleared (
provider_cleared: trueplus the codes to restore and a⚠️ Billing Provider — CLEARED BY THIS WRITEsummary 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 and 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 leadingpayprefix is stripped automatically; any other non-numeric value is rejected.
dry_run: Defaults tofalse. Whentrue, returns the unapply call that would be made without executing it.move_balance_to_insurance: Defaults totrue— after the unapply succeeds, the charge’s remaining patient balance and patient portion are moved to the insurance balance/portion. Set tofalseto only unapply.charge_id: AdvancedMD charge ID the payment was applied to (fromadvancedmd_find_charge’sehr_charge_id). Required unlessmove_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_chargereturns both (ehr_patient_id,ehr_visit_id).
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
- Use
advancedmd_find_chargeto locate the charge — itsmatch.applied_paymentslists every payment applied to the charge, and each entry’spayment_idis what this tool takes (ehr_charge_idis thecharge_id). Confirm the amount and check number match the payment that should be unapplied, and skip entries withvoid != "0". - Call
advancedmd_unapply_paymentwithdry_run=trueand review the endpoint preview. - Call again with
dry_run=false(or omitdry_run) to unapply. - Confirm
unappliedistrueand 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).- Use
advancedmd_find_chargeto get the charge (ehr_charge_id, plusehr_patient_id/ehr_visit_id) and the applied payment’spayment_id. - Call
advancedmd_unapply_paymentwithpayment_idandcharge_id. - 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.
- Confirm
balance_transfer.transferredin the output and the before/after amounts in the summary table.
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 toalready_unapplied: true, and it then retries only the balance move.retryable: false(reason: "payment_detail_fid_constraint") — AdvancedMD rejected the move with itsPD_FIDReferencesconstraint onactv_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.
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.- Unapply the payment with
advancedmd_unapply_payment. - The payment becomes an unapplied credit on the patient account.
- Re-apply it to the correct charge with AdvancedMD Post Payment, passing the correct
charge_idand amounts.
Missing AdvancedMD Connector Flow
Use this when the tool reportsadvancedmd_not_connected.
- Do not retry with the same inputs immediately.
- Ask the user to either connect AdvancedMD and try again, or stop the task.
- After AdvancedMD is connected, call the tool again.
Dry-Run Example
Unapply + Move Balance Example (default)
Unapply-Only Example
Error Handling
The tool returns a structured tool error for invalid inputs (missing or non-numericpayment_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.
- payment no longer applied → treated as already unapplied (
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:trueonly whendry_run=falseand 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:truewhen 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 withmove_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 (plusreasonandsuggested_actionswhenretryableisfalse); or{status: "planned"}on dry runs.