California Vehicle Registration Status Check
Purpose
Given a California license plate plus one secondary identifier (last 5 of the VIN, the registered owner's last name, or the company/lessor name), return the vehicle's current California DMV registration status, expiration date, fees owed, and any holds. Read-only — the skill stops at the results page; it never advances into the renewal-payment flow.
The lookup is the CA DMV's free public "Vehicle Registration Status" service, a server-rendered Java/Struts (.do) flow gated by AWS WAF + CloudFront. It works without an account and is the only first-party way to verify CA registration status without making a Public Records Request.
When to Use
- A driver/owner agent verifying whether their plate is currently registered before a road trip or smog check.
- A fleet/lessor agent reconciling registration status across multiple CA plates.
- A used-car-buying agent confirming a seller's claim that the registration is "current" before exchanging money.
- Any agent that needs "registered? yes/no + expiration date" for one CA plate without paying for a commercial VIN service.
Do not use this skill for:
- Out-of-state plates (CA-only).
- Title status, lienholder info, or registered-owner identity disclosure — those require a formal Vehicle Record Request (different skill, fees apply, requires a justified purpose under CA Vehicle Code).
- Driver's-license status — separate DMV tool.
Workflow
The flow is a three-step server-rendered form. There is no public JSON API for registration status — confirmed by surveying the dmv.ca.gov surface. Everything goes through wasapp/rsrc/vrapplication.do. Use a remote Browserbase session with --proxies --verified because the WAF is sensitive to datacenter IPs and missing browser fingerprints.
1. Open a Verified + residential-proxy session
SID=$(browse cloud sessions create --keep-alive --proxies --verified | jq -r .id)
export BROWSE_SESSION="$SID"
A bare session (no --proxies) regularly trips the AWS WAF banner ("Your request has been blocked...") on the first POST. The --verified flag (Verified browsers) is what passes the fingerprint check during form submission. Both are required — do not omit either.
2. Navigate directly to step 1 (license-plate form)
browse open --remote "https://www.dmv.ca.gov/wasapp/rsrc/vrapplication.do"
browse wait load --remote
browse snapshot --remote
Skip the marketing portal page (/portal/vehicle-registration/vehicle-registration-status/) — its only useful link is /wasapp/rsrc/vrapplication.do, so going direct saves one navigation and one cookie round-trip.
The form has a single visible input:
| Field | Selector | Constraint |
|---|---|---|
| License plate | input#licensePlateNumber (name licensePlateNumber) | maxlength 8, pattern [a-zA-Z0-9]*, no spaces / special chars |
Fill it and submit:
browse fill --remote "input#licensePlateNumber" "8ABC123"
browse click --remote 'button[value="Continue"]' # name="method" value="Continue"
browse wait load --remote
The form POSTs to itself (action="/wasapp/rsrc/vrapplication.do", method=post) with method=Continue and licensePlateNumber=<value>. The backend issues a 302 to step 2.
3. Step 2 — secondary identifier
Step 2 asks for one of:
- Last 5 digits of the VIN (or HIN for vessels)
- Registered owner's last name (private ownership)
- Company / lessor name (leased or company-owned)
Pick whichever the user supplied. The form is a radio-group + single text input. Snapshot and use refs:
browse snapshot --remote # find the radio for the chosen identifier
browse click --remote '@<radio-ref>' # select identifier type
browse fill --remote 'input[type="text"]' "12345" # the secondary value
browse click --remote 'button[value="Continue"]'
browse wait load --remote
If the user gives "last 5 of VIN", strip dashes/spaces and uppercase. The DMV accepts only alphanumerics here.
4. Step 3 — extract status
The results page is keyed off plate + identifier match. Read the page text:
browse get markdown body --remote
browse screenshot --remote --path screenshots/04-results.png
Map the visible content to the output schema in §Expected Output. Don't click "Renew Registration" / "Pay Now" / any forward CTA — read-only.
5. Release the session
browse cloud sessions update "$SID" --status REQUEST_RELEASE
Site-Specific Gotchas
- No public JSON/GraphQL API exists. Surveyed
dmv.ca.govandwasapp/*paths — every registration-status route is server-rendered Struts. Confirmedwasapp/ipp2/initRegInfoInquiry.do(a sometimes-cited "alt" endpoint) returns 404 as of the build date. Don't waste time looking for a faster surface; the only path is the form flow. - The form is two pages, not one. Step 1 takes only the license plate (
input#licensePlateNumber, maxlength 8). Step 2 asks for one secondary identifier. New agents commonly stub a one-shot POST with both fields — that returns the step-1 page again because the secondary input doesn't exist in the step-1 form bean. - Submit-button selector. The Continue button is
button[name="method"][value="Continue"]— the button is the form's onlyname="method"element, and its value is what drives Struts dispatch. Targetingbutton[type="submit"]works too but is less specific. - CloudFront / WAF behavior. The pages are fronted by CloudFront with
x-frame-options: SAMEORIGINand an active AWS WAF rule (AWSALB,AWSALBCORS,PD_STATEFUL_*,TS01dc4fc6cookies are issued on every visit and must be preserved across the multi-step flow). Browserbase's session model handles this automatically; if you're rolling your own HTTP client you must keep the cookie jar. - No CAPTCHA observed on step 1. The form has no visible CAPTCHA on initial load. Aggressive POST repetition (>~5 within a minute from the same fingerprint) trips a WAF block page with "Your request has been blocked..." — back off and let the residential proxy rotate.
autocomplete="off"everywhere. The form sets autocomplete off; if youbrowse filland the input still appears empty on snapshot, the page may be using a non-standard CA-DMV input wrapper. Usebrowse press Tabafter fill to force the blur event before clicking Continue.- Plate format. California plates are 1–7 chars (modern: 7 alphanumeric, older: shorter). Max 8 is just the input maxlength. Strip dashes, spaces, and any special chars before filling. Kid-plate symbols (heart, star, hand, plus) are not enterable here — the DMV portal warns "For Kids Plates, please leave out any symbols."
- Step-2 invalid-secondary path. If the plate exists but the secondary identifier doesn't match, the result page renders a generic "We could not locate a record matching the information provided" — this is
not_foundfrom the user's perspective, but it is not proof the plate is unregistered (could be a typo in the secondary). Surface this distinction in the output. - Cross-jurisdiction. Non-CA plates always render
not_found. There is no way to look up registration status for a non-CA vehicle on dmv.ca.gov. For non-CA plates, the agent should refuse and recommend the relevant state's DMV. - Don't follow the "Renew" CTA. The success page includes a "Renew Registration Now" link that goes to
/wasapp/vrir/start.do(the renewal flow). Read-only skill — stop at the status page. - Spanish locale.
dmv.ca.gov/portal/es/vehicle-registration/vehicle-registration-status/exists, but the actual/wasapp/rsrc/vrapplication.doform is English-only. Localization happens only on the portal page, not the underlying app.
Expected Output
Return a JSON object with one of the following shapes.
Success — current registration
{
"success": true,
"license_plate": "8ABC123",
"registration_status": "current",
"expiration_date": "2026-08-31",
"fees_due_usd": 0,
"holds": [],
"raw_status_text": "Your vehicle registration is current. Expires: 08/31/2026.",
"renewal_available": false,
"source_url": "https://www.dmv.ca.gov/wasapp/rsrc/vrapplication.do"
}
Success — expired or pending renewal
{
"success": true,
"license_plate": "8ABC123",
"registration_status": "expired",
"expiration_date": "2025-04-30",
"fees_due_usd": 312.00,
"holds": [
{"type": "smog_certification_required", "detail": "Smog certification is required to renew."}
],
"raw_status_text": "Your registration expired on 04/30/2025. Renewal fees: $312.00. Smog certification required.",
"renewal_available": true,
"source_url": "https://www.dmv.ca.gov/wasapp/rsrc/vrapplication.do"
}
Not found — plate / secondary mismatch
{
"success": true,
"license_plate": "8ABC123",
"registration_status": "not_found",
"expiration_date": null,
"fees_due_usd": null,
"holds": [],
"raw_status_text": "We could not locate a record matching the information provided.",
"renewal_available": false,
"source_url": "https://www.dmv.ca.gov/wasapp/rsrc/vrapplication.do",
"note": "Plate may exist but the secondary identifier (VIN-last-5 / last-name / company-name) didn't match. Re-prompt the user."
}
Site-blocked / WAF wall
{
"success": false,
"error_reasoning": "waf_blocked",
"raw_status_text": "Your request has been blocked. ... Request ID: <hex>",
"source_url": "https://www.dmv.ca.gov/wasapp/rsrc/vrapplication.do",
"remediation": "Rotate the residential proxy and retry once after 60s. Confirm `--verified` Verified browsers is enabled."
}
Tool offline / maintenance
{
"success": false,
"error_reasoning": "service_unavailable",
"raw_status_text": "<verbatim DMV maintenance banner>",
"source_url": "https://www.dmv.ca.gov/wasapp/rsrc/vrapplication.do"
}
Out-of-state plate (user error)
{
"success": false,
"error_reasoning": "non_ca_plate",
"raw_status_text": null,
"remediation": "This skill only checks California plates. For other states use that state's DMV registration lookup."
}