RPM / RTM / CCM Reimbursement Calculator
Estimate reimbursement based on the CPT codes achieved or projected per patient per month.
Tip: Each program’s CPT rows are in a section below—click the program name (e.g. Remote Patient Monitoring) to expand or collapse that table. Open the section you need to enter counts. The totals at the bottom always include all programs.
Tip: Rate rows are grouped by program—click the program heading to show or hide that program’s table. Use this to focus on one section at a time. Changes are kept in this browser only (see the note at the bottom).
* Overridden from default. Rates are saved in this browser only and do not affect other visitors or the site admin's settings.
Developer API
This calculator is designed to be embedded in another application (iframe or pop-up). Approved parent apps can push data in and receive live billing totals back out. All integration is origin-gated: only hosts on the admin allowlist at Settings → RPM / RTM / CCM Calc can send to or receive from the calculator.
Every path below (URL prefill, inbound
postMessage, and outbound postMessage) is rejected unless the calling origin's hostname is enabled on the admin allowlist at WP Admin → RPM / RTM / CCM Calc → Approved domains. Until your host is added there:
- Page loads with no browser
Origin/Refererthat maps to an allowlisted host: allrrc_*prefills are ignored (treats the visit as a public viewer). - Inbound
postMessageactions (setCounts,setProjection,setView,reset,ping) are silently dropped. - Outbound
postMessageevents (ready/update/pong) are never emitted to your window — no listener in your app will fire.
Push data into the calculator via URL query string
The calculator parses rrc_* query params on both the server (PHP) and the client (window.location.search). That means prefill still works even when a page cache, CDN, or optimizer strips query args before PHP ever sees them.
Origin header, then Referer, or the rrc_calc_trusted_origin_host filter (reverse proxy). The legacy parameter rrc_domain is ignored if present. After load, the parent app can also establish the origin via postMessage (browser-validated event.origin).
Your domain must be whitelisted at WP Admin → RPM / RTM / CCM Calc → Approved domains. Prefill and outbound messaging unlock when the request’s trusted origin (or a later postMessage sender) matches an enabled allowlist entry.
Supported query parameters
| Parameter | Required? | Type / allowed values | Default | What it does | Example |
|---|---|---|---|---|---|
rrc_<CPT> |
optional | Non-negative integer. <CPT> must be 5 digits and part of the plugin catalog (RPM, RTM, CCM, PCM, BHI). Unknown or invalid codes are silently dropped. |
0 | Prefills the Per-Code Achieved input for one specific CPT code. Values accumulate with rrc_achieved; when both are present, the explicit rrc_<CPT> param wins for that code. |
rrc_99454=120 |
rrc_achieved |
optional | Comma-separated list of CPT:count pairs, URL-encoded. Whitespace is tolerated; invalid segments are skipped silently. |
empty | Compact alternative to rrc_<CPT>. Use it when you need to prefill many codes in one parameter (keeps the URL short and survives CDN query-arg limits better). |
rrc_achieved=99454:120,99457:80,99458:30 |
rrc_view |
optional | One of code, patient, rates, api. Any other value is ignored. |
code |
Selects which tab is active on first render. code = Per-Code Achieved, patient = Per-Patient Projection, rates = Edit Rates (localStorage), api = Developer API docs. |
rrc_view=patient |
rrc_readonly |
optional | Truthy flag: any of 1, true, yes, on. Anything else = off. |
0 (off) |
Locks the Per-Code numeric inputs and hides the Reset button. Ideal for dashboard embeds where the host app owns the state and the calculator is view-only. Tabs, tooltips, and projection mode remain interactive. | rrc_readonly=1 |
Deprecated: rrc_domain is not listed above because v1.4.7+ does not read it. Old bookmarks may still include &rrc_domain=…; it is ignored. Identity always comes from Origin / Referer (or postMessage after load).
Parsing rules, at a glance: values in rrc_* params are parsed as UTF-8 URL-decoded strings; negative numbers, non-numeric values, and unknown CPT codes are ignored; rrc_<CPT> overrides any conflicting entry in rrc_achieved; state is applied after the catalog is built so rate overrides stay intact; no cookie, session, or database write is performed by any rrc_* parameter.
Example URLs
1. Minimal prefill (single code). Open from your whitelisted app (so Origin / Referer is your app’s host):
https://your-wp-site.example/calculator/?rrc_99454=120
Replace older docs that used ...?rrc_domain=app.wellnessmetric.net&rrc_99454=120 — v1.4.7+ drops rrc_domain entirely, so the same scenario is: ...?rrc_99454=120 (your app host stays on the allowlist; the browser sends it, not the query string).
2. Full dashboard embed (read-only, Per-Code tab, multiple codes). Host app has already validated the numbers and just wants the calculator to render a snapshot:
https://your-wp-site.example/calculator/?rrc_view=code&rrc_readonly=1&rrc_achieved=99454:120,99457:80,99458:30
Breakdown: the app’s hostname must be on the allowlist (detected from the request, not the URL).
rrc_view=code → open on the Per-Code Achieved tab.
rrc_readonly=1 → lock the numeric inputs and hide Reset.
rrc_achieved → prefill 99454 with 120, 99457 with 80, and 99458 with 30.
Same scenario, sample app.example.com + response body
Example path (replace your-wp-site.com and calculator-page with your real WordPress URL). activeDomain below is the trusted origin app.example.com:
https://your-wp-site.com/calculator-page/?rrc_view=code&rrc_readonly=1&rrc_achieved=99454:120,99457:80,99458:30
When the calculator loads inside an approved embedding (trusted Origin / Referer), it emits the same shape as the 5. Live response preview box. Below is a static example; dollar amounts use the built-in default rates (admin and per-domain overrides can change them). data.event is ready on first load and update on each recalculation. In a live message, the code array under each program normally includes every catalog CPT with count and subtotal — the trimmed JSON shows only the three codes with activity, plus the three program shells.
{
"statusCode": 200,
"resFlag": true,
"type": "I",
"msg": "calculator billing data returned successfully.",
"data": {
"source": "rpm-rtm-ccm-calculator",
"event": "update",
"currency": "$",
"timestamp": "2026-04-24T15:00:00.000Z",
"activeDomain": "app.example.com",
"summary": {
"totalClaims": 230,
"totalAmount": 10146.70,
"monthlyRevenue": 0.00,
"annualRevenue": 0.00,
"perPatientPerMonth": 0.00,
"patients": 0
},
"data": [
{
"program": "RPM",
"label": "Remote Patient Monitoring (RPM)",
"codes": [
{ "code": "99454", "description": "Monthly review of RPM data (16+ days / 30)", "rate": 43.02, "count": 120, "subtotal": 5162.40 },
{ "code": "99457", "description": "Patient-provider communication, RPM data (20 min)", "rate": 47.87, "count": 80, "subtotal": 3829.60 },
{ "code": "99458", "description": "Additional 20 min RPM communication (add-on)", "rate": 38.49, "count": 30, "subtotal": 1154.70 }
],
"totalClaims": 230,
"totalAmount": 10146.70,
"perPatientProjection": {
"intensity": "typical",
"track": "standard",
"patients": 0,
"monthlyRevenue": 0.00,
"annualRevenue": 0.00,
"perPatientPerMonth": 0.00
}
},
{
"program": "RTM",
"label": "Remote Therapeutic Monitoring (RTM)",
"codes": [],
"totalClaims": 0,
"totalAmount": 0.00,
"perPatientProjection": { "intensity": "typical", "track": "msk", "patients": 0, "monthlyRevenue": 0.00, "annualRevenue": 0.00, "perPatientPerMonth": 0.00 }
},
{
"program": "CCM",
"label": "Chronic / Principal Care Management (CCM / PCM / BHI)",
"codes": [],
"totalClaims": 0,
"totalAmount": 0.00,
"perPatientProjection": { "intensity": "typical", "track": "standard", "patients": 0, "monthlyRevenue": 0.00, "annualRevenue": 0.00, "perPatientPerMonth": 0.00 }
}
]
}
}
Your listener on window.addEventListener('message', ...) receives this object on event.data when the embedding origin and allowlist are valid. summary.totalAmount = 120×43.02 + 80×47.87 + 30×38.49. Per-patient fields stay at zero until you enter data on the Per-Patient Projection tab.
3. Open on the projection tab with no prefill (sales or onboarding demo):
https://your-wp-site.example/calculator/?rrc_view=patient
4. Mix compact and per-code forms. The explicit rrc_99454 wins over the value in rrc_achieved:
https://your-wp-site.example/calculator/?rrc_achieved=99454:100,99457:80&rrc_99454=150
Effective result: 99454 = 150, 99457 = 80.
your-wp-site.example is the WordPress host that serves the shortcode. Your app’s hostname (for allowlisting and data.activeDomain) is read from the browser Origin / Referer, not from a query parameter.
Push data into the calculator at runtime (postMessage)
Domain must be whitelisted. Inbound messages whose event.origin hostname is not on the admin allowlist are silently dropped — no error, no state change.
After the calculator is loaded (in an iframe or opened window), your app can send messages to drive it. The calculator only accepts messages whose event.origin hostname is on the allowlist.
Every inbound message must be an object with target: "rpm-rtm-ccm-calculator" and an action:
| action | Payload | Effect |
|---|---|---|
setCounts | { counts: { "99454": 120, ... } } | Sets Per-Code input values, then recalculates. Codes not in the catalog are ignored. |
setProjection | { projection: { intensity, tracks: {rpm, rtm, ccm}, patients: {RPM, RTM, CCM} } } | Sets the Per-Patient tab intensity / track / patient counts, then recalculates. |
setView | { view: "code"|"patient"|"rates"|"api" } | Switches the active tab. |
reset | (none) | Resets all counts on both tabs to 0. |
ping | (none) | Triggers a fresh outbound response without changing state — useful right after load. |
Push example:
// From your app (iframe is embedded on a page on your app origin):
var iframe = document.getElementById('rrc-iframe');
iframe.contentWindow.postMessage({
target: 'rpm-rtm-ccm-calculator',
action: 'setCounts',
counts: { '99454': 120, '99457': 80, '99458': 30 }
}, 'https://your-wp-site.com');
Receive billing data from the calculator (postMessage)
Domain must be whitelisted. The outbound targetOrigin is strictly https://<activeDomain> where activeDomain is an entry from the admin allowlist. If your app's host isn't on the list, no message is ever dispatched to your window — your listener will never fire.
Every time the user edits inputs, the calculator emits a message to window.opener and window.parent using a strict targetOrigin of https://<activeDomain> (the allowlisted app host from the request or postMessage). Listen for it in your app:
window.addEventListener('message', function (event) {
if (event.origin !== 'https://your-wp-site.com') return;
var res = event.data;
if (!res || !res.data || res.data.source !== 'rpm-rtm-ccm-calculator') return;
if (res.resFlag !== true) {
console.warn('Calculator error', res.msg);
return;
}
// res.data is an object with meta + nested .data[] programs.
console.log('event:', res.data.event, 'summary:', res.data.summary);
res.data.data.forEach(function (program) {
console.log(program.program, program.totalAmount, program.perPatientProjection);
});
});
Response envelope (v1.4.0+) — the top level is strictly {statusCode, resFlag, type, msg, data}. All meta and the program array live inside data:
{
"statusCode": 200,
"resFlag": true,
"type": "I",
"msg": "calculator billing data returned successfully.",
"data": {
"source": "rpm-rtm-ccm-calculator",
"event": "update",
"currency": "$",
"timestamp": "2026-04-24T12:34:56.789Z",
"activeDomain": "app.example.com",
"summary": {
"totalClaims": 250,
"totalAmount": 12345.67,
"monthlyRevenue": 5000.00,
"annualRevenue": 60000.00,
"perPatientPerMonth": 100.00,
"patients": 50
},
"data": [
{
"program": "RPM",
"label": "Remote Patient Monitoring (RPM)",
"codes": [
{ "code": "99453", "description": "RPM device set up", "rate": 19.32, "count": 0, "subtotal": 0.00 },
{ "code": "99454", "description": "Monthly review of RPM data (16+ days / 30)", "rate": 43.02, "count": 120, "subtotal": 5162.40 }
],
"totalClaims": 120,
"totalAmount": 5162.40,
"perPatientProjection": {
"intensity": "typical",
"track": "standard",
"patients": 50,
"monthlyRevenue": 2500.00,
"annualRevenue": 30000.00,
"perPatientPerMonth": 50.00
}
},
{ "program": "RTM", "label": "Remote Therapeutic Monitoring (RTM)", "codes": [ /* ... */ ], "totalClaims": 0, "totalAmount": 0, "perPatientProjection": { /* ... */ } },
{ "program": "CCM", "label": "Chronic / Principal Care Management (CCM / PCM / BHI)", "codes": [ /* ... */ ], "totalClaims": 0, "totalAmount": 0, "perPatientProjection": { /* ... */ } }
]
}
}
statusCode— HTTP-style code.200on success,400on validation error.resFlag—trueif the payload is usable,falseotherwise.type—"I"info / success,"E"error.msg— human-readable status string.data.source— always"rpm-rtm-ccm-calculator". Use this in your listener to filter out unrelated messages.data.event—"ready"(initial emit on load),"update"(user edit),"pong"(reply toping), or"error"(inbound action failed).data.currency— currency symbol the calculator was rendered with.data.timestamp— ISO-8601 UTC timestamp.data.activeDomain— the approved host the calculator is talking to, ornull.data.summary— cross-program totals.data.data[]— always three entries in order: RPM, RTM, CCM. Programs your shortcode filtered out still appear with zero totals and emptycodes.
Full working examples
Iframe embed + bi-directional messaging
<iframe id="rrc" width="100%" height="900"
src="https://your-wp-site.com/calculator/?rrc_readonly=1&rrc_view=code"></iframe>
<script>
var WP_ORIGIN = 'https://your-wp-site.com';
var iframe = document.getElementById('rrc');
// Receive live totals.
window.addEventListener('message', function (e) {
if (e.origin !== WP_ORIGIN) return;
var res = e.data;
if (!res || !res.data || res.data.source !== 'rpm-rtm-ccm-calculator') return;
document.getElementById('total').textContent = '$' + res.data.summary.totalAmount.toFixed(2);
});
// Push initial counts as soon as the iframe is ready.
iframe.addEventListener('load', function () {
iframe.contentWindow.postMessage({
target: 'rpm-rtm-ccm-calculator',
action: 'setCounts',
counts: { '99454': 120, '99457': 80 }
}, WP_ORIGIN);
});
</script>
Pop-up window + deep-link
var url = 'https://your-wp-site.com/calculator/' +
'?rrc_view=patient' +
'&rrc_achieved=99454:120,99457:80,99458:30';
var pop = window.open(url, 'rrc', 'width=1000,height=900');
window.addEventListener('message', function (e) {
if (e.origin !== 'https://your-wp-site.com') return;
var res = e.data;
if (!res || !res.data || res.data.source !== 'rpm-rtm-ccm-calculator') return;
console.log('event:', res.data.event, 'summary:', res.data.summary);
res.data.data.forEach(function (p) {
console.log(p.program, p.totalAmount);
});
});
Live response preview
This box shows the exact JSON payload the calculator is currently emitting. Edit the Per-Code or Per-Patient tabs and watch it update live.
{}
Tip: if the data.activeDomain field above is null, the page load had no allowlisted Origin/Referer (or cache hid it) and the parent has not yet sent a postMessage. Add your app’s hostname to WP Admin → RPM / RTM / CCM Calc → Approved domains and open the calculator from that app, or have your parent send {target:"rpm-rtm-ccm-calculator",action:"ping"} from an allowlisted origin.
RPM and RTM descriptions are sourced from telehealth.hhs.gov. CCM descriptions reflect CMS/AMA CPT guidance. Estimates are for planning only — actual reimbursement depends on payer, documentation, medical necessity, locality adjustments, and the current CMS Physician Fee Schedule. Verify all CPT code rules and rates before submitting claims.