Skip to content

fix(candid): warn when a claim's resubmission code can't be sent to Candid - #436

Open
bexport-gh wants to merge 2 commits into
Medical-Software-Foundation:mainfrom
bexport-gh:fix/candid-resubmission-code-warning
Open

fix(candid): warn when a claim's resubmission code can't be sent to Candid#436
bexport-gh wants to merge 2 commits into
Medical-Software-Foundation:mainfrom
bexport-gh:fix/candid-resubmission-code-warning

Conversation

@bexport-gh

@bexport-gh bexport-gh commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What's wrong

Canvas exposes a CMS-1500 box 22 resubmission code on every claim coverage (6 corrected, 7 replacement, 8 void), along with a Payer ICN field and a "Display CORRECTED CLAIM on HCFA header" checkbox. On a Claim.MD instance that code does real work. On a Candid instance it does nothing at all, and nothing tells you so.

A biller sets the code to 7, saves, re-queues the claim, gets a green result, and the claim files with the payer as an ordinary original claim. No error, no warning, no comment. The correction they think they filed does not exist.

Why there's no passthrough fix

POST /api/encounters/v4 has no claim-frequency, corrected-claim, or original-reference field anywhere in its 52 top-level fields. The only claim_frequency_code in the schema hangs off external_claim_submission.submission_records[], which is for reporting claims filed outside Candid, the opposite of what this plugin does.

Candid confirmed this directly:

The correct workflow here is to submit corrected claims in the Candid UI after the original claim is adjudicated (to avoid getting duplicate denials). There is no way to do this via API. Voids, corrected claims, and fresh claims all follow the same workflow in Candid. Candid populates the correct frequency code based on the type of submission selected.

So this isn't a gap we can close by wiring up a field. Corrections are a Candid UI workflow by design, and this PR makes the dead end visible instead of silent.

What this does

Adds resubmission_code_comment() to effect_helpers.py and calls it from CandidSubmitAPI.post(). When any active coverage on the claim carries a resubmission code, the plugin appends a claim comment:

Resubmission code 7 (replacement of prior claim) is set on this claim. Candid has no API field for it, so it was not sent and this claim was filed as an original. Corrections and voids are submitted in the Candid UI, where Candid sets the frequency code from the submission type you choose. Wait for the original claim to adjudicate before submitting the correction, otherwise it can come back as a duplicate denial.

The adjudication ordering is load-bearing, not padding. Correcting before the original adjudicates draws the exact duplicate denial the workflow exists to avoid, so the comment carries the ordering rather than just the destination. There's a test asserting on it so it can't quietly drop out of the string later.

The comment fires on success and on failure, because a biller expecting a correction needs to know either way.

The payload is untouched. This is additive only, so it cannot regress any existing submission.

Why warn and not block

Blocking was the first instinct, and the data argued against it. Five instances running this plugin (four production, one sandbox) already have resubmission codes sitting on coverages. Those codes are inert today. If submission started failing whenever one was present, the next re-queue on those production instances would break. A comment can't do that to anyone.

Worth flagging for whoever picks this up: those production customers have the same silent problem, they just haven't noticed yet. Support is following up with them separately.

What this deliberately doesn't do

It doesn't touch the resubmission controls in home-app. Hiding them on Candid instances is the obvious next thought and it's a trap: the same field drives box 22 on the printed CMS-1500, and a Candid customer can still legitimately print and mail a paper corrected claim. That trade-off needs a product decision, tracked separately.

Testing

uv run pytest tests/ -v in extensions/candid gives 142 passed (135 existing + 7 new).

  • 5 in tests/test_effect_helpers.py covering the helper: no code set, a single code, multiple coverages listed primary-first, coverages without codes ignored, and an unmapped code falling back to its raw value
  • 2 in tests/test_submit.py covering the wiring: the comment lands in the returned effects with the payload unchanged, and an ordinary claim gets no extra comment

Verified non-vacuous by mutation. Neutering resubmission_code_comment to always return None fails 5 of the 7. The 2 that still pass are the negative tests, which is the correct result.

Files

  • extensions/candid/candid/effect_helpers.py - new RESUBMISSION_CODE_LABELS + resubmission_code_comment()
  • extensions/candid/candid/api/submit.py - call it from post()
  • extensions/candid/candid/README.md - document the limitation under Claim Submission
  • extensions/candid/tests/test_effect_helpers.py - 5 tests
  • extensions/candid/tests/test_submit.py - 2 tests

Generated from a Support Stack investigation.

Canvas stores a CMS-1500 box 22 resubmission code (6 corrected,
7 replacement, 8 void) on each claim coverage, but Candid's submission
path has no field to carry it. POST /encounters/v4 exposes no
claim-frequency or original-reference field, and the only
claim_frequency_code in the schema sits under external_claim_submission,
which reports claims filed outside Candid.

The result was silent. A biller could set code 7, save, re-queue, and
watch the claim file with the payer as an ordinary original claim with
no error, warning, or comment anywhere.

Add resubmission_code_comment() and call it from CandidSubmitAPI.post()
so a claim carrying a code gets a comment explaining that it filed as an
original and the correction has to be made in Candid. Fires on success
and on failure, since a biller expecting a correction needs to know
either way.

The payload is unchanged, so this is additive only and cannot regress an
existing submission. Warning rather than blocking is deliberate:
instances already carry inert codes on coverages, and failing their next
re-queue would be a worse outcome than an extra comment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bexport-gh

Copy link
Copy Markdown
Contributor Author

cerberus review

@canvas-cerberus canvas-cerberus Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐶 Cerberus review

0 important · 0 nit · 0 pre-existing

No blocking issues found.


Review cost: $0.63.

Candid confirmed there is no API path for corrected claims at all. Voids,
corrections, and fresh claims all go through the same Candid UI workflow,
Candid derives the frequency code from the submission type the biller
selects, and the original claim has to adjudicate first or the correction
returns as a duplicate denial.

The previous comment said "make the edit in Candid and resubmit from
there," which was directionally right but left out the ordering. A biller
following it literally could submit the correction before adjudication
and draw the exact denial the workflow exists to avoid.

Spell out the whole workflow in the comment and the README, and assert on
the adjudication ordering in the test so the guidance can't quietly drop
out of the string later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bexport-gh

Copy link
Copy Markdown
Contributor Author

cerberus review

@canvas-cerberus canvas-cerberus Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐶 Cerberus review

0 important · 0 nit · 0 pre-existing

No blocking issues found.


Review cost: $0.43.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant