Skip to main content

Check timely filing alerts

Outcome​

Claims at risk of missing the payer's filing deadline are surfaced and actioned before the window expires.

Prerequisites​

Where the filing window comes from​

One tier order answers this question for both surfaces: the AR alert below, and the claim scrub that raises TIMELY_FILING_EXPIRED and blocks the claim. They used to answer separately and could disagree on the same claim (#1330).

TierSourceKeyed onWins when
1tenant config.program_config.timely_filing_dayspayer_id + program_id + claim_type, effective on the claim's service_fromthe newest effective row carries a positive value
2master rcm_master.payer_submission_capability.timely_filing_dayspayer_id + claim_typetier 1 answered nothing; the longest of the payer's submission routes governs
3——nothing is configured: the window is 0, no alert fires and no claim is blocked

Three consequences worth knowing before you triage:

  • Tier 1 is the contract-specific override an operator sets. It is the tenant-owned, effective-dated row; tier 2 is the payer-wide default.
  • A tier-1 row for the other claim_type falls through to tier 2 — it does not resolve. So an institutional claim is never measured against the professional window.
  • Tier 3 means "do not run the rule", never "use a default". An unconfigured payer is silent on both surfaces rather than blocking claims on an unknown number.

The full triage procedure for a window that looks wrong is §RCM-2 of the engineering runbook (docs/operations/runbook-rcm.md in the repo).

Alert levels​

The claim service tracks days elapsed from the claim's service_from against the resolved filing window.

LevelThresholdAction
WARNING80% of filing window elapsedPrioritize for submission
ALERT90% of filing window elapsedSubmit immediately
EXPIRED100% of filing window elapsedMay require appeal for late filing

Query alerts​

# Get all alerts (filterable by alertLevel, payerId, billingEntityId)
curl "http://localhost:3000/api/v1/claims/timely-filing-alerts?alertLevel=ALERT"

Or in SQL for ad-hoc analysis. Master and tenant are separate physical databases, so this is two queries, not one join — run the first against the master connection and paste its rows into the second.

Query 1 — tier 2, against the master database. One row per payer and claim type, taking the longest configured submission route, which is the fold the resolver applies:

SELECT payer_id,
claim_type,
max(timely_filing_days) AS timely_filing_days
FROM rcm_master.payer_submission_capability
WHERE timely_filing_days IS NOT NULL
AND timely_filing_days > 0
GROUP BY payer_id, claim_type
ORDER BY payer_id, claim_type;

Query 2 — the whole tier order, against the tenant database. Paste query 1's output into master_window:

WITH master_window (payer_id, claim_type, timely_filing_days) AS (
VALUES
-- one row per line from query 1
('c0000001-0000-4000-a000-000000000001'::uuid, 'P', 90)
),
tier1 AS (
-- The NEWEST effective row per claim, chosen BEFORE the window predicate:
-- a present row that leaves timely_filing_days empty falls through to tier 2
-- rather than reaching back to a superseded number.
SELECT DISTINCT ON (c.claim_id)
c.claim_id,
ppc.timely_filing_days
FROM billing.claim c
JOIN config.program_config ppc
ON ppc.payer_id = c.payer_id
AND ppc.program_id = c.program_id
AND ppc.claim_type = c.claim_type
AND ppc.effective_from <= c.service_from
AND (ppc.effective_to IS NULL OR ppc.effective_to >= c.service_from)
ORDER BY c.claim_id, ppc.effective_from DESC
),
resolved AS (
SELECT c.claim_id,
c.status_current,
c.payer_id,
c.claim_type,
c.service_from,
CURRENT_DATE - c.service_from AS days_elapsed,
CASE
WHEN t1.timely_filing_days > 0 THEN 'tier 1 — tenant override'
WHEN mw.timely_filing_days IS NOT NULL THEN 'tier 2 — master capability'
ELSE 'tier 3 — not configured, rule does not run'
END AS window_source,
COALESCE(
CASE WHEN t1.timely_filing_days > 0 THEN t1.timely_filing_days END,
mw.timely_filing_days
) AS filing_deadline_days
FROM billing.claim c
LEFT JOIN tier1 t1
ON t1.claim_id = c.claim_id
LEFT JOIN master_window mw
ON mw.payer_id = c.payer_id
AND mw.claim_type = c.claim_type
WHERE c.status_current IN ('CREATED', 'BUILT', 'SCRUBBING',
'SCRUBBED', 'SCRUB_FAILED', 'VALIDATED')
)
SELECT claim_id, status_current, payer_id, claim_type, service_from,
days_elapsed, filing_deadline_days, window_source,
ROUND(days_elapsed::numeric / filing_deadline_days * 100, 1) AS pct_used
FROM resolved
WHERE filing_deadline_days IS NOT NULL
AND days_elapsed::numeric / filing_deadline_days >= 0.8
ORDER BY pct_used DESC;

A claim with no program_id cannot match a tier-1 row at all and resolves from master — the LEFT JOIN above reproduces that, as does the service.

Validation​

CheckExpected
ALERT row countTrending down
EXPIRED row countZero (or all routed to appeal)
Contract-specific window (tier 1)A config.program_config row exists for that payer, program and claim_type, effective on the service date
Payer-wide default (tier 2)rcm_master.payer_submission_capability.timely_filing_days matches the contract for each claim_type
window_source on a spot-checked claimThe tier the operator believes is governing

Troubleshooting​

SymptomCauseFix
No alerts but claims should be expiringNeither tier resolves a positive window — timely_filing_days is NULL on both, so tier 3 silences the ruleSet per Configure a new payer.
Alerts use the wrong window for institutional claimsThe tier-1 row carries claim_type = 'P'; an I claim falls through to masterSet the tier-2 row for claim_type = 'I', or add a tier-1 row on the I claim type.
The override is ignoredThe tier-1 row is not effective on the claim's service_from, or names a different program_idCheck effective_from / effective_to and the program on the claim.
Alert and scrub disagree on the numberShould no longer happen — both read one resolver (#1330)Follow §RCM-2 of the engineering runbook and capture window_source for the claim.
Late-filing appeals stuckCO-29 handler missingSee Auto-correction operator playbook.

Cross-references​

Next​

7.3 — Refresh reporting views