Check timely filing alerts
Outcome
Claims at risk of missing the payer's filing deadline are surfaced and actioned before the window expires.
Prerequisites
claims.readto query.- Nightly job runner active (see Run batch operations).
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).
| Tier | Source | Keyed on | Wins when |
|---|---|---|---|
| 1 | tenant config.program_config.timely_filing_days | payer_id + program_id + claim_type, effective on the claim's service_from | the newest effective row carries a positive value |
| 2 | master rcm_master.payer_submission_capability.timely_filing_days | payer_id + claim_type | tier 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_typefalls 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.
| Level | Threshold | Action |
|---|---|---|
WARNING | 80% of filing window elapsed | Prioritize for submission |
ALERT | 90% of filing window elapsed | Submit immediately |
EXPIRED | 100% of filing window elapsed | May 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
| Check | Expected |
|---|---|
ALERT row count | Trending down |
EXPIRED row count | Zero (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 claim | The tier the operator believes is governing |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| No alerts but claims should be expiring | Neither tier resolves a positive window — timely_filing_days is NULL on both, so tier 3 silences the rule | Set per Configure a new payer. |
| Alerts use the wrong window for institutional claims | The tier-1 row carries claim_type = 'P'; an I claim falls through to master | Set the tier-2 row for claim_type = 'I', or add a tier-1 row on the I claim type. |
| The override is ignored | The tier-1 row is not effective on the claim's service_from, or names a different program_id | Check effective_from / effective_to and the program on the claim. |
| Alert and scrub disagree on the number | Should 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 stuck | CO-29 handler missing | See Auto-correction operator playbook. |
Cross-references
- Configure a new payer for filing-window setup.
- Commercial billing-NPI & filing.