Claim Chaser Docs
Step 0 — From nothing to a working API key
Who this is for: a developer who has never used Claim Chaser and has no account. If you already have a login and an organization, skip to Getting a key.
Time: about 5 minutes, all self-serve. You do not need to contact sales.
The mental model
Four things must exist, in this order, before an API key can be issued:
your user account → your organization → signed legal agreements → API key
(email + password) (name + address) (BAA, Terms, Privacy) (cc_live_…)
Each one gates the next. You cannot reach the Developer tab until the first three are done, and the Developer tab is the only place a key can be created.
Steps 1–4 happen in a browser. There is no API for them, by design — the legal agreement in Step 4 has to be accepted by a person. Only Step 5 onward is scriptable.
Step 1 — Create your account
Go to app.claimchaser.ai/login.
There is no separate sign-up page. Under the login form is the line "Don't have an account? Sign up" — click Sign up. The form switches to "Create your account" and takes an email address and a password.
⚠️
app.claimchaser.ai/signupand/registerare not valid URLs. They redirect to/login, which looks like a rejection. Use the Sign up toggle on the login page itself.
Submit, and you will see:
Please check your email to confirm your account. If you don't see it, check your spam folder.
Step 2 — Confirm your email
You will receive "Confirm Your Signup" from noreply@claimchaser.ai, normally within seconds.
⚠️ Open the link in the same browser you signed up in
The confirmation link is tied to the browser session that created the account. If you sign up on your laptop and open the email on your phone, the link will fail with a "PKCE code verifier not found" error, and the account cannot be confirmed.
If that happens, open the link in the browser you signed up in — the same machine and the same browser profile. That recovers the account; you do not need to start over.
Clicking the link confirms your account, signs you in, and drops you straight into Step 3.
Step 3 — Create your organization
You will land on /organizations/create automatically. Every other page redirects here until it
is done.
| Field | Notes |
|---|---|
| Your Name | The person creating the account. |
| Organization Name | Your company or practice group. This is the tenant that owns all your data. |
| Organization Address | Free text. This is not an Office address — offices are separate records you create later, each with their own address, EIN and callback number. |
All three are required. Creating an organization does not create an Office or a Doctor; your org starts completely empty.
An organization is your tenant boundary. Your API key will be bound to exactly this organization and can never see or touch another one. If you are an OEM partner provisioning multiple practices, see One organization per practice.
Step 4 — Accept the legal agreements
Next you will be sent to /organizations/accept-baa, which blocks the entire application —
including the Developer tab — until completed.
Three separate acceptances are required:
- Business Associate Agreement (BAA) — required for HIPAA. Claim Chaser handles Protected Health Information on your behalf, so this agreement is between Claim Chaser LLC and your organization as the Covered Entity.
- Terms and Conditions
- Privacy Policy
Tick all three and submit. This is enforced server-side; all three are genuinely required.
Who should click this? Whoever is authorised to bind your organization to a HIPAA agreement — not necessarily the engineer doing the integration.
Step 5 — Generate your API key
You now have access to the app. Go to Developer in the left-hand navigation (app.claimchaser.ai/developer).
Give the key a name, then choose one access level:
| Choose | You get |
|---|---|
read | all read endpoints |
write | read + claim upload/update + provisioning (offices, doctors, carrier requests, credentialing) |
calls:write | read + write + triggering calls |
These are cumulative tiers, not a multi-select. Picking
calls:writegrantsreadandwriteas well — the key comes back with"scopes": ["read","write","calls:write"]. Choose the lowest tier that covers what you need.
If you choose calls:write, you also set a daily call cap (1–500, default 50). Calls cost real
money; set this low while you are building. A cap of 1 is a good safety net during development.
🔑 The key is shown exactly once
The full key (format
cc_live_…) is displayed once, at creation. Claim Chaser stores only a hash and cannot show it to you again. Copy it before leaving the page. If you lose it, revoke it and create another.
Step 6 — Verify it works
export CLAIM_CHASER_API_KEY="cc_live_YOUR_KEY_HERE"
curl -s -H "x-api-key: $CLAIM_CHASER_API_KEY" \
https://app.claimchaser.ai/api/uploads/context
A working key on a brand-new organization returns your org id, empty lists for the records you have not created yet, and the full shared carrier catalogue:
{
"organization_id": "…",
"offices": [],
"doctors": [],
"carriers": [ { "id": "…", "name": "Aetna", "in_network": false, … } ],
"valueMappings": []
}
Two things worth noting from this response:
officesanddoctorsare empty — that is correct. Creating them is the next guide.carriersis not empty — the carrier catalogue is shared across all customers.in_networktells you whether your organization is linked to that carrier yet.
claim-chaser whoamidoes not work with an API key — it uses the interactive login path and will report "Not logged in". Use a keyed read like the call above to verify a key.
You are done. Continue to the API onboarding guide to create your first office, doctor and claim.
Troubleshooting
"Too many email requests. Please wait N seconds"
Resend is rate-limited to roughly one email per minute. The message names the exact wait. Wait it out and resend — the email arrives.
"PKCE code verifier not found in storage"
You opened the confirmation link in a different browser, a different device, or after clearing cookies. Open the same link again in the browser you signed up in and it will confirm normally.
"This confirmation link has expired"
Confirmation links are time-limited. Use "Resend confirmation email" to get a fresh one — it is rate-limited to about one per minute, so if you are told to wait, wait and try again.
403 {"error":"Forbidden: Invalid origin"}
You are calling a Developer-page endpoint (/api/developer/…) directly. Those are browser-only and
CSRF-protected. Key creation is not scriptable — use the Developer page.
401 {"error":"Unauthorized"}
Missing, malformed, revoked, or expired key. Check you are sending the header x-api-key — not
Authorization: Bearer.
What you cannot do with an API key
A person must do these in the app. There is no key-reachable endpoint:
- Creating an account, an organization, or accepting the legal agreements (this page)
- Creating, revoking, or re-capping API keys
- Registering webhook endpoints or rotating their signing secrets
- Editing the carrier catalogue or carrier phone numbers, or turning a carrier on/off for your org
- Turning voice calling on or off
One organization per practice
If you are provisioning multiple practices (an OEM integration), each practice is a separate organization with its own API key, and each is its own Covered Entity under the BAA in Step 4.
The practice's authorised signatory must accept the BAA (Step 4). A partner cannot accept it on a practice's behalf unless a separate written agreement expressly permits it. Steps 1–4 are browser-only, so provisioning a new practice is not fully headless; if you need an arrangement to complete them for a practice, contact us before onboarding practices.
Appendix — corrections to the existing /docs/api §10 script
The current "full journey, end to end" script does not run as written. Corrected version:
export KEY="cc_live_YOUR_KEY_HERE"; export BASE="https://app.claimchaser.ai"
# 1. office — NOTE: "create": true is REQUIRED.
# Without it, a new organization gets {"matched":false,"candidates":[]} and NOTHING is created.
OFFICE_ID=$(curl -sX POST -H "x-api-key: $KEY" -H "Content-Type: application/json" $BASE/api/offices \
-d '{"name":"Main Street Clinic","address":"123 Main St","callback_number":"2085551234","ein":"123456789","create":true}' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
# 2. doctor — "create": true likewise. npi is REQUIRED (10 digits), unlike in the app.
DOCTOR_ID=$(curl -sX POST -H "x-api-key: $KEY" -H "Content-Type: application/json" $BASE/api/doctors \
-d "{\"name\":\"Dr. Smith\",\"npi\":\"1234567890\",\"office_id\":\"$OFFICE_ID\",\"create\":true}" \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
# 3. carrier — ⚠️ CHECK match.exact BEFORE USING THE RESULT.
# This endpoint fuzzy-matches. A request for a carrier not in the catalogue can silently
# return a DIFFERENT carrier. Only trust the result when "exact": true.
curl -sX POST -H "x-api-key: $KEY" -H "Content-Type: application/json" $BASE/api/carrier-requests \
-d '{"requested_name":"Cigna Dental"}'
# → {"matched":true,"carrier_id":"…","carrier_name":"Cigna Dental","match":{"exact":true,"score":1}}
# If "exact" is false, do NOT use carrier_id — look the carrier up in GET /api/carriers instead.
# 4. credential — NOTE: "kind" is REQUIRED. Keyed callers send "customer".
curl -sX POST -H "x-api-key: $KEY" -H "Content-Type: application/json" $BASE/api/doctor-carriers/turn-on \
-d "{\"doctor_id\":\"$DOCTOR_ID\",\"carrier_id\":\"$CARRIER_ID\",\"kind\":\"customer\"}"
# 5. upload the claim
# NOTE: claim_status is matched case-insensitively; "Denied", "DENIED" and "denied" all work.
# An unrecognised status is dropped (claim defaults to Open, which is dialable) and reported on the
# response in ignored_status_values: [{ row, claim_number, value }], not stored blindly.
curl -sX POST -H "x-api-key: $KEY" -H "Content-Type: application/json" $BASE/api/uploads/claims \
-d "{\"action\":\"insert\",\"claims\":[{\"patient_name\":\"Jane Doe\",\"subscriber_id\":\"W123\",\"carrier_id\":\"$CARRIER_ID\",\"office_id\":\"$OFFICE_ID\",\"doctor_id\":\"$DOCTOR_ID\",\"date_of_birth\":\"1985-04-12\",\"date_of_service\":\"2026-05-01\",\"claim_status\":\"Denied\"}]}"
# 6. confirm callable (no dial, no bill)
curl -s -H "x-api-key: $KEY" "$BASE/api/claims/$CLAIM_ID/callable"
Other corrections needed in the existing docs
| Doc statement | Reality |
|---|---|
match_type is "npi" / "name" | Returns "exact", "identifier", "fuzzy_auto" |
dispatch_blockers "possible values are VOICE_OFF and HOLIDAY_WEEKEND_FLOOR" | Eight values, and knowing which one you have is not enough — see the section below |
event_types drawn from two values | Three: carrier.request_resolved is also valid |
| "You can save a provider before you have their NPI" | True in the app, false in the API — npi is required |
claim_status "Accepts the app's status strings" | Accepts any string; only the exact-cased documented values are dialable |
Nothing documents POST /api/claims/delete | It exists and is partner-reachable |
Nothing documents POST /api/uploads/value-mappings | It exists (201), lowercases input_value, and is only mentioned inside another endpoint's error message. Results are readable via valueMappings in GET /api/uploads/context |
Reading dispatch_blockers — what to do, not just what it means
Placed here because it is the first thing people need after they get a key, and it is not documented anywhere else yet. Its permanent home is the API Reference.
GET /api/claims/{id}/callable and the make-call dry run both return a dispatch_blockers array.
"callable": false is usually not a problem. What matters is which group the blocker is in,
because the three groups need three different responses from you.
You do not need a
calls:writekey to check callability.GET /api/claims/{id}/callableworks withread. Only themake-calldry run needscalls:write— which is why the CLI'sclaims callablecommand requires it, since that wrapper callsmake-call. If you are only checking whether a claim is ready, use the endpoint directly.
1 · Fix your claim — these are about your data
| Blocker | Meaning |
|---|---|
MISSING_FIELDS | the claim lacks data a call needs |
STATUS_NOT_DIALABLE | claim_status isn't a dialable value (e.g. a terminal status like Paid) |
NO_CALLABLE_PHONE | no usable insurance_phone, and none resolvable from the carrier |
PAID | a payment amount is recorded, so we will not chase it |
2 · Wait — these clear on their own
| Blocker | Clears when |
|---|---|
CARRIER_HOURS | the carrier's calling hours start |
HOLIDAY_WEEKEND_FLOOR | the next business day |
COOLDOWN | the cooldown window since the last call elapses |
3 · Someone must act — this one never clears by waiting
| Blocker | What to do |
|---|---|
VOICE_OFF | Voice calling is switched off for the organization. Turn it on — you can do this with your API key. |
⚠️
VOICE_OFFwill not clear on its own — but you can clear it in one requestNo amount of polling changes it. It stays until voice is switched on. You do not need the app and you do not need a person — your existing
write-scope key can do it:curl -sX POST -H "x-api-key: $KEY" -H "Content-Type: application/json" \ $BASE/api/voice-settings -d '{"enabled": true}'{"success":true,"enabled":true}Check the current state any time:
curl -s -H "x-api-key: $KEY" $BASE/api/voice-settings{"enabled":false,"schedule_last_turned_on_at":null,"schedule":{ … }}The field is
enabled— sendingvoice_onreturns400 {"error":"enabled must be a boolean"}.⚠️ Turning voice on starts real calls and real spend for every claim in the organization that is already callable. Confirm with the practice before you flip it, and use
GET /api/claims/{id}/callablefirst to see what would go out.This is the blocker that costs people time:
VOICE_OFFsits alongside genuinely temporary blockers likeCOOLDOWN, so it looks like something that will resolve itself. It will not — and unlike the others, it is entirely in your hands.
A correctly-provisioned claim on an organization that has not enabled calling yet returns:
{"callable":false,"dispatch_blockers":["COOLDOWN","VOICE_OFF"]}
Nothing is wrong with that claim. COOLDOWN clears by itself; VOICE_OFF is waiting on you.