irs.gov

check-refund-status

Installation

Adds this website's skill for your agents

 

Summary

Look up a federal tax-refund's current stage at IRS Where's My Refund, given SSN/ITIN, filing status, refund amount, and tax year. Returns the stage (Return Received / Refund Approved / Refund Sent), deposit/mail date when surfaced, Tax Topic codes (152/151/203), and the canonical status-page URL. Read-only and PII-sensitive — never logs or persists the TIN.

FIG. 01
FIG. 02
FIG. 03
FIG. 04
SKILL.md
297 lines

IRS Where's My Refund — Check Refund Status

Purpose

Given a taxpayer's Social Security Number or ITIN, filing status, and exact whole-dollar refund amount, query the IRS "Where's My Refund?" tool at https://sa.www4.irs.gov/wmr/ and return the current refund stage (Return Received / Refund Approved / Refund Sent), the estimated or confirmed deposit/mail date when surfaced, any IRS message codes shown (Tax Topic 152, 151, 203, math-error codes), and the canonical status-page URL the user can visit themselves.

Read-only. This skill handles deeply sensitive PII (the TIN is a US national identifier). The caller MUST pass inputs over a secure channel; the skill MUST NOT log, persist, screenshot-as-text, or transcribe the SSN/ITIN anywhere — including trace files, browser-trace exports, sandbox stdout, or screenshot OCR layers. Treat the SSN like a session-only memory write that gets zeroed when the task ends.

When to Use

  • A user wants to know where their 1040 refund is in the IRS pipeline ("Has my refund been approved? When is the deposit coming?").
  • A tax-help agent needs to determine whether a refund delay is normal (Topic 152), under further review (Topic 151), or being offset by another federal/state debt (Topic 203).
  • A status-check flow that triages whether to recommend the taxpayer call IRS (1-800-829-1954) or initiate a refund trace (Form 3911).
  • Do not use for: amending a return, filing, modifying direct-deposit info, checking amended-return status (use the separate "Where's My Amended Return?" tool at sa.www4.irs.gov/wmar/), or stimulus/EIP lookups.

Workflow

The recommended method is browser driving the public form. The internal API endpoint exists at POST https://sa.www4.irs.gov/api/taxpayers/accounts/wmr/1.0/refundSummary and the request shape is fully known (see "Direct API path — confirmed-fragile, do not use" below), but cookieless POSTs from a non-warmed session are gated by Akamai bot management and require a per-session X-IRS-Session-Id shared-secret hashed from a client-side randomUUID. The browser flow is shorter, more honest, and surfaces all of the same response data through window.__REDUX_STORE__.refundResult.refundSummary.

1. Create a Verified + residential-proxy session

sid=$(browse cloud sessions create --keep-alive --verified --proxies | jq -r .id)
export BROWSE_SESSION="$sid"

Both --verified (Verified browsers: residential-class fingerprint, plausible WebGL/canvas/UA stack) and --proxies (residential IP) are mandatory. A bare or datacenter-proxy session gets either an Akamai 403 / Access-Denied HTML page, or the SPA loads but the first form-submit POST returns HTTP 400/401 because the Akamai sensor fingerprint is missing.

2. Open the landing page and wait for the React SPA to hydrate

browse open "https://sa.www4.irs.gov/wmr/" --remote
browse wait load --remote
browse wait timeout 3000 --remote        # SPA mounts + Akamai sensor script POSTs
browse snapshot --remote

The HTML shell is a 1.6-KB Create-React-App template; all form markup is injected by /wmr/assets/index-kzMzosx0.js (about 572 KB, may version-rotate — confirm by reading the <script type="module" src="/wmr/assets/index-*.js"> tag). The first 1–3 seconds after load are the Akamai sensor handshake; if you submit before it completes, the next POST returns 400.

3. Step 1 form: TIN, tax year, filing status

The form is a two-step wizard. Step 1 collects all four inputs and is submitted by the button-continueToStepTwo button (DOM id — also visible as a snapshot ref labelled "Continue" / "Submit"). Fields:

InputForm field nameFormat
SSN or ITINssnInput9 digits, dashes allowed; client regex /^[0-9-_*]{0,21}$/ (it accepts asterisks because the input masks digits as you type)
Tax yeartaxYearInput2 (radio group)4-digit year, typically <current_year-1> or <current_year-2>
Filing statusfilingStatusInput (radio group)enum below
Refund amountrefundAmountInputWhole dollars; client regex /^[$]?[\d,]{0,13}$/; on blur the SPA strips the $ and commas

Filing-status enum — this is the IRS-internal mapping read off the React bundle. Note that value 4 is intentionally absent (legacy 1040 line):

User-facing labelAPI value
Single"1"
Married Filing Jointly"2"
Married Filing Separately"3"
Head of Household"5"
Qualifying Surviving Spouse (a.k.a. Qualifying Widow(er))"6"

If the caller hands you the user-facing label, map it to the API value before filling. If they hand you a digit "4", reject with a validation error — the form will silently coerce to a wrong status if you let it through.

browse fill input[name=ssnInput] "$SSN" --remote          # never echo $SSN to stdout
browse click "radio: 2024" --remote                       # tax year (verify against snapshot)
browse click "radio: Single" --remote                     # filing status label
browse fill input[name=refundAmountInput] "1234" --remote
browse click "button-continueToStepTwo" --remote
browse wait timeout 4000 --remote                         # API round-trip + nav

4. Read the result page and decode the status

On a successful submit the SPA navigates to /wmr/results (or /wmr/es/results for Spanish). The result data is fully present in the Redux store. Read it once, do not poll:

RESULT_JSON=$(browse eval --remote 'JSON.stringify(window.__REDUX_STORE__?.getState?.().refundResult ?? null)')

If window.__REDUX_STORE__ is undefined (the SPA does not always expose its store on window), fall back to reading the DOM:

browse snapshot --remote
# Look for these texts in the rendered page:
#   "Return Received"   (stage 1)
#   "Refund Approved"   (stage 2)
#   "Refund Sent"       (stage 3)
# and any of:
#   "Tax Topic 152"  (normal processing, generic information)
#   "Tax Topic 151"  (under review — refund may be reduced or held)
#   "Tax Topic 203"  (offset for past-due federal/state debts, child support, etc.)
#   "Mailing date:"  / "Direct deposit date:"  / "Sent by mail:"  / a "Get refund status" date string

The Redux store keys (extracted from the bundle):

refundResult: {
  sharedSecretToken: "<random-uuid>",  // session-only — DROP, never log
  sessionId:         "<hashed token>", // session-only — DROP, never log
  httpStatusCode:    "",
  refundSummary: {
    tin:                  "<echoed back, last 4 only on display>",
    filingStatus:         "1"|"2"|"3"|"5"|"6",
    refundAmount:         "1234",
    adjustedRefundAmount: "",          // populated if IRS recalculated
    mailingDate:          "",          // populated when stage = Refund Sent
    statusCode:           "C..."|"H..."|"R..."|...,
    conditionCode:        "",
    mathErrorCodes:       null,
    balanceDueAmount:     "",
    taxPeriod:            "<YYYY>12",  // taxYear + "12"
    totalBfsOffsetAmount: "",
    noticeDate:           "",
    dueByDate:            "",
    traceSummary:         null | { status: "F3911_ELIGIBLE"|"EXISTING_CLAIM_LT_45_DAYS"|"EXISTING_CLAIM_GTE_45_DAYS"|"CHKCL_ELIGIBLE"|"INELIGIBLE", ... }
  }
}

Map refundSummary.statusCode.charAt(0) to the user-facing stage:

First charStage
CRefund Approved (Check issued / Credit posted)
HRefund Held (Tax Topic 151 / 203 territory — flag for review)
RRefund Sent / Return Received family — disambiguate via the SPA's translation file txp__wmr__<statusCode> or simply read the page banner
any otherUse the page-rendered banner text directly; do not guess

5. Build the canonical status-page URL

https://sa.www4.irs.gov/wmr/refund_status is the canonical landing-page URL the user should visit. The results URL (/wmr/results) is not linkable by a third party — it only renders after a fresh form submission inside the same browser session. Return the landing-page URL to the caller along with the data; instruct them they will need to re-enter the same three inputs to see the live page themselves.

6. Release the session — and never persist anything

browse cloud sessions update "$sid" --status REQUEST_RELEASE
# Wipe in-memory copies of the SSN before returning
unset SSN

Do not leave the session in RUNNING state; the next agent run might reuse it and inherit the Akamai cookies tied to this lookup.

Direct API path — confirmed-fragile, do not use

For completeness, the API call the SPA makes under the hood is:

POST https://sa.www4.irs.gov/api/taxpayers/accounts/wmr/1.0/refundSummary
Content-Type: application/json
X-IRS-Session-Id: <sha-256(crypto.randomUUID() + Date.now() + tin) — see ty() in the bundle>
X-IRS-System-Id:  WMR-UI

{"tin":"123456789","taxYear":"2024","filingStatus":"1","refundAmount":"1234"}

Direct GET to the same path returns 405 Method Not Allowed with an <Exception> body that reveals the API-gateway service name and operation — confirming the path is correct. POSTing the body directly from a cookieless client returns either 400 (Akamai sensor headers absent) or, sporadically, a 401. Reproducing the SPA's full Akamai-blessed POST out-of-band is brittle and worth no engineering effort over the browser flow. Don't waste time on it.

Site-Specific Gotchas

  • PII handling, non-negotiable. Do not log the SSN/ITIN. Do not include it in browser-trace exports, summary.md, autobrowse turn logs, screenshots that capture the form, sandbox stdout, or any error trace. Mask all but the last 4 digits in any operator-facing surface (***-**-1234). Do not commit a test SSN to a repo. Do not ship a screenshot showing the form fields populated.
  • Verified + residential proxy is mandatory. A bare or datacenter-proxy session gets Akamai-served Access Denied HTML on first navigation, or — if the shell loads — a 400/401 on the first form submit. Verified: the response shell ships an Akamai sensor script (bazadebezolkohpepadr=, /akam/13/..., the obfuscated /1ytYJb/... script) and three Akamai cookies (_abck, bm_sz, ak_bmsc).
  • Filing-status 4 is not used. The IRS 1040 codes are 1, 2, 3, 5, 6 with 4 absent. If the caller passes a 4, refuse rather than coerce.
  • Tax year is a required input, not implied. The form has a radio group (taxYearInput2) with two or three years (typically last filing year and the prior one). If the caller didn't supply a year, ask before submitting — IRS responds 204 ("no records match") if you guess wrong, and the no-match is indistinguishable from a wrong-SSN error.
  • HTTP 204 = silent no-match. A 204 response from refundSummary means the (tin, filingStatus, refundAmount, taxYear) tuple did not match IRS records. The SPA renders "We cannot provide any information about your refund." This is the same UI as a real-but-not-yet-posted return; surface both possibilities to the caller. Do not retry — repeated 204s on the same IP trigger Akamai rate-limiting (429).
  • HTTP 422 / 503 are soft-errors. The SPA navigates to /wmr/results with an empty refundSummary and shows a generic "system error, try again later" banner. Do not treat as auth failure.
  • HTTP 429 = IP rate-limited. Akamai-side, not API-gateway-side. Back off with a fresh proxy IP, not a fresh session on the same IP. Document as a soft-fail and return the partial data you have.
  • One query per session, period. The IRS WMR is rate-limited per IP and per session. Do not chain multiple lookups; create a fresh session (and a fresh residential exit) for each query. Re-using a session that just queried SSN A to query SSN B is the fastest way to get the proxy IP burned.
  • The results URL is not bookmarkable. /wmr/results only renders after an in-session form submission. The link you give back to the user is the landing page /wmr/refund_status (or /wmr/); they will need to re-enter the three inputs to see it themselves.
  • Stage prefix enum r6 = ["C", "H", "R"]. These are the first letters of refundSummary.statusCode for "normal" stage banners. Any other prefix (B*, D*, K*, WB, WM, WP, WS, TPNC, etc.) maps to a specific message-code translation file txp__wmr__<code>; read the page banner text rather than trying to decode the prefix yourself.
  • Tax Topic codes are surfaced in plain text, not in refundSummary. Topic 152 (normal processing — appears for ~99% of in-progress refunds), Topic 151 (return under further review — possible reduction or audit), Topic 203 (refund offset for child support, federal student loans, state income tax, etc.) appear as anchor links in the banner. Extract them by grepping the page text after the stage banner; don't assume they are in the API JSON (they aren't).
  • Refund Trace (Form 3911) subflow. When refundSummary.traceSummary.status is F3911_ELIGIBLE or CHKCL_ELIGIBLE, the SPA renders a "Start a refund trace" button that navigates to /wmr/claim. Do not click it — that initiates an IRS workflow. Just surface the eligibility flag to the caller as trace_eligible: true.
  • Bundle filename rotates. /wmr/assets/index-kzMzosx0.js is the bundle hash as of 2026-05-18; it rotates on every IRS deploy. Read the <script type="module" src> from the shell at runtime rather than hardcoding. The field names, enum values, and API path inside the bundle have been stable across IRS releases for the past 2 years.
  • Spanish localization mirror. /wmr/es/refund_status and /wmr/es/results are the Spanish-language equivalents. Field names and API shape are identical. Only switch domains/paths if the caller explicitly requests Spanish output; the IRS lookup itself is language-agnostic.
  • Don't bother with the API directly. GraphQL-style introspection / direct POST to /api/taxpayers/accounts/wmr/1.0/refundSummary is confirmed gated by Akamai sensor headers — even with the correct X-IRS-Session-Id token, a cookieless POST is rejected as 400 or 401. Page-warm a real browser session, fill the form, let the SPA make the request inside its sensor-blessed context. The browser path is the supported path.
  • No autobrowse / browser-trace verification done. This SKILL.md was authored from static analysis of the React bundle (verified API path, request body, field names, validation regexes, HTTP-status branches, response shape, status-code prefix enum, filing-status enum) plus a direct browse cloud fetch of the WMR landing page. The skill was not driven end-to-end against a real refund because (a) doing so requires a live SSN, which is unacceptable PII to inject into an eval harness, and (b) the generator sandbox does not have outbound network access to connect.usw2.browserbase.com (CDP/WebSocket required for browse open --remote). The first agent to invoke this skill on a real user query and a CDP-reachable sandbox should verify the snapshot ref names in Workflow step 3, then promote from candidate to launched.

Expected Output

Five distinct outcome shapes. All shapes echo back the last 4 digits of the TIN only, never the full TIN.

// Stage 1 — Return Received
{
  "success": true,
  "stage": "Return Received",
  "stage_code": "R...",
  "tin_last4": "1234",
  "filing_status": "Single",
  "refund_amount_dollars": 1234,
  "tax_year": "2024",
  "estimated_completion": null,
  "tax_topics": ["152"],
  "math_error_codes": [],
  "trace_eligible": false,
  "status_page_url": "https://sa.www4.irs.gov/wmr/refund_status"
}
// Stage 2 — Refund Approved (deposit date surfaced)
{
  "success": true,
  "stage": "Refund Approved",
  "stage_code": "C...",
  "tin_last4": "1234",
  "filing_status": "Married Filing Jointly",
  "refund_amount_dollars": 4567,
  "adjusted_refund_amount_dollars": null,
  "tax_year": "2024",
  "expected_deposit_date": "2026-05-22",
  "delivery_method": "direct_deposit",
  "tax_topics": ["152"],
  "trace_eligible": false,
  "status_page_url": "https://sa.www4.irs.gov/wmr/refund_status"
}
// Stage 3 — Refund Sent
{
  "success": true,
  "stage": "Refund Sent",
  "stage_code": "C...",
  "tin_last4": "1234",
  "filing_status": "Head of Household",
  "refund_amount_dollars": 2890,
  "adjusted_refund_amount_dollars": 2890,
  "tax_year": "2024",
  "sent_date": "2026-05-15",
  "delivery_method": "mailed_check",
  "tax_topics": ["152"],
  "trace_eligible": true,
  "trace_status": "F3911_ELIGIBLE",
  "status_page_url": "https://sa.www4.irs.gov/wmr/refund_status"
}
// Held / under review — Topic 151 or 203 surfaced
{
  "success": true,
  "stage": "Refund Held",
  "stage_code": "H...",
  "tin_last4": "1234",
  "filing_status": "Single",
  "refund_amount_dollars": 3210,
  "adjusted_refund_amount_dollars": 1850,
  "tax_year": "2024",
  "tax_topics": ["151", "203"],
  "balance_due_amount_dollars": 0,
  "total_bfs_offset_amount_dollars": 1360,
  "math_error_codes": ["08", "12"],
  "notice_date": "2026-05-10",
  "trace_eligible": false,
  "status_page_url": "https://sa.www4.irs.gov/wmr/refund_status"
}
// Soft-fail outcomes — no-match, rate-limited, system error, anti-bot block
{
  "success": false,
  "reason": "no_match" | "rate_limited" | "system_error" | "anti_bot_block" | "validation_error",
  "http_status": 204 | 400 | 401 | 422 | 429 | 500 | 503 | null,
  "user_message": "We cannot provide any information about your refund. Verify the SSN, filing status, refund amount, and tax year, then try again later.",
  "tin_last4": "1234",
  "status_page_url": "https://sa.www4.irs.gov/wmr/refund_status"
}