A deterministic, §6694-disciplined tax-compute API for human-led product and engineering teams. Tax MCP computes only inside the published supported envelope, returns deterministic declines or needs_review when review is safer, and makes billed, retry_safe, next_action, reason, and provenance fields part of the product contract.
Use Tax MCP when an accountable product owner needs reproducible tax-compute behavior in a bounded workflow. Agent workflows are valid only under accountable human product owners. The docs start with scope and trust because unsupported cases should be discovered through docs, OpenAPI, and the capability manifest before a customer depends on runtime behavior.
Reference links: GET /api/mcp/tax-capabilities for the supported envelope, OpenAPI for request and response schema, roadmap feedback, changelog/release notes, support/contact, and terms.
Run the first safe call, then inspect decline and needs_review examples.
Operational guides for response interpretation, review UI, envelope use, and retries.
A guided path from zero to a first computed call, a first deterministic decline, and where to get help. Each step links to its full section below; the standalone route is Run the first safe call.
Authorization: Bearer header over HTTPS, never commit it to source, put it in a URL, or paste it into feedback or a support email.X-Quota-* / X-RateLimit-* headers (see Rate limits & usage headers). At the cap you get a 429 — no overage, no surprise bill.POST to /api/mcp/tax-compute/federal returns a full computed result (Compute federal tax). A computed 200 is one billable call.200 with computed:false, a reason, and a next_action (Try a decline). This is a safety success, not a failure or an outage: the engine refuses to guess a wrong-but-plausible number (§6694). It is not something you did wrong. Whether it will ever compute depends on your INPUT — a bare retry of the same request re-declines deterministically (a decline is a billable 200, like a compute); see every response state for bills?/retry-safe?/next-action per outcome.Tax MCP is coverage-bounded: it computes inside a documented supported envelope and returns a deterministic unsupported or needs_review decline (with a reason and next_action) when a case is outside it — never a wrong-but-plausible number.
Machine-readable: GET /api/mcp/tax-capabilities returns the full supported envelope (years, statuses, modeled fields, out-of-scope, decline codes, billing semantics) — no auth, no payloads.
The engine computes documented federal, CA, NY, NYC, reconcile, what-if, and review-packet slices when the request includes the required facts. Examples include ordinary tax, selected credits, selected Schedule A, bounded Schedule C/E, capital-loss carryover, selected Schedule D / 8949, special-rate gains, and affirmed positive §1256 aggregates. The authoritative field list is the capabilities endpoint.
Many paths compute only after explicit §6694 affirmations such as final-net Schedule C/E, clean covered brokerage, special-rate portions, state conformity, or state-specific adjustment assertions. Missing or contradictory affirmations return a deterministic decline instead of a guessed tax number.
Unsupported years, states, source facts, dirty lots, missing affirmations, over-threshold QBI, broad K-1 / partnership / S-corp surfaces, and source-document upload-to-compute all fail closed with a reason, retry_safe, and next_action. A decline is a final safety answer, not an outage.
needs_reviewReconcile can return needs_review when the engine computed independently but the source set or filed-return comparison needs a human. It is not a filing conclusion, not a blame assignment, and not a compute input promotion from uploaded documents.
Full return preparation, filing, broad document ingestion to compute, broad Schedule A / K-1 / partnership / S-corp / Form 1116 / part-year state support, and public one-click marketplace connectors are not shipped. The roadmap separates shipped, certifying, planned, and blocked work.
| Supported today | Out of scope (declines deterministically) |
|---|---|
W-2 wage workflows with standard or itemized deductions (Schedule A: SALT with the OBBBA-2025 cap + home mortgage interest with the §163(h)(3) $750k limit — supply mortgageAvgBalance; greater-of standard is chosen automatically); documented taxable interest / ordinary dividend facts; selected child/dependent and foreign-tax-credit slices; bounded Schedule C/E paths with required affirmations; capital-loss carryover, selected clean covered capital-gain facts, special-rate gain portions, and affirmed positive §1256 aggregates; §199A QBI deduction for REIT dividends (1099-DIV box 5) and below-threshold affirmed Schedule C QBI only. Selected federal (ordinary tax, NIIT, Additional Medicare, preferential LTCG/QDIV) and CA / NY / NYC computation paths. Non-mutating reconcile (match / mismatch / unsupported / needs_review). | Full-return preparation or filing; credits beyond the documented child/dependent, ACTC, and §904(j) FTC slices; itemized deductions beyond SALT + mortgage interest (medical, charitable, etc.); broad payments/refund workflows; dirty or unsupported Schedule D / Form 8949 residuals; Schedule C/E cases missing required affirmations or outside the bounded final-net paths; pass-through K-1 / partnership / S-corp income and business/PTP QBI; broad multi-status state coverage; ambiguous or corrected source documents outside the documented envelope. |
Need a workflow that isn't modeled yet? Tell us what you were trying to compute — it helps us prioritize the roadmap. (The engine declines outside the envelope rather than guess.)
Sign inand create a key from your dashboard. Keys are shown once — copy it immediately. Your plan's monthly call limit is shared across all your keys.
Send your key as a Bearer token on every request:
Authorization: Bearer taxmcp_YOUR_KEYcurl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"year": 2025,
"filingStatus": "single",
"wages": 120000,
"taxableInterest": 800
}'The same first safe call in Python (requests):
import requests, uuid
resp = requests.post(
"https://tax-mcp.com/api/mcp/tax-compute/federal",
headers={
"Authorization": "Bearer taxmcp_YOUR_KEY",
"Content-Type": "application/json",
"Idempotency-Key": str(uuid.uuid4()), # safe retries, bills once
},
json={"year": 2025, "filingStatus": "single", "wages": 85000,
"medicareWages": 85000, "federalWithholding": 9500},
)
data = resp.json()
if data.get("computed"):
print("total tax:", data["totalTax"], "owed/refund:", data.get("owedOrRefund"))
else: # a deterministic decline is a FINAL answer, not an error — do not blind-retry
print("declined:", data["code"], "->", data["next_action"])Supply at least one income field. Amounts are whole-dollar — the engine applies the form's rounding. An HTTP 200 is billable whether the engine computed a result or honestly declined.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/mcp/tax-compute/federal | Selected federal paths — ordinary tax, NIIT, Additional Medicare, LTCG/QDIV (declines outside coverage) |
| POST | /api/mcp/tax-compute/state | Selected CA / NY compute paths (set state); deterministic decline outside coverage |
| POST | /api/mcp/tax-compute/reconcile | Non-mutating, coverage-bounded reconcile vs a filed return (match / mismatch / unsupported / needs_review) |
| GET | /api/mcp/tax-compute/{federal,state,reconcile} | Capability / discovery (free — no quota charge) |
Copy-paste-ready recipes for every modeled combination. Each request body is complete, and the # -> line is the actual live response (captured from https://tax-mcp.com) — not illustrative. Swap in your key and run. Every billable POST shows the Idempotency-Key header (safe retries, bills once).
The simplest compute — one wage figure. Standard deduction is applied automatically (greater-of vs itemized).
curl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"year":2025,"filingStatus":"single","wages":75000}'
# -> 200 {"computed":true, "taxableIncome":59250, "totalTax":7949, "next_action":"none"}Qualified dividends get the preferential rate. Rule: qualifiedDividends must be ≤ ordinaryDividends (it is a subset of them).
curl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"year":2025,"filingStatus":"single","wages":90000,
"taxableInterest":1200,"ordinaryDividends":4000,"qualifiedDividends":4000}'
# -> 200 {"computed":true, "taxableIncome":79450, "totalTax":12113}filingStatus is one of single · married_filing_jointly · married_filing_separately · head_of_household.
curl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"year":2025,"filingStatus":"married_filing_jointly","wages":180000}'
# -> 200 {"computed":true, "taxableIncome":148500, "totalTax":22498}Schedule-C income needs a §6694 affirmation that it is a single sole-proprietor final net — absent it, the engine declines rather than guess. To make it compute, add scheduleCIsSingleSolePropFinalNetAffirmed:true AND socialSecurityWages (SE tax needs the wage base). Discover the exact affirmations at GET /api/mcp/tax-capabilities (affirmations).
curl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"year":2025,"filingStatus":"single","wages":40000,"scheduleCIncome":30000}'
# -> 200 {"computed":false, "code":"schedule_c_needs_review", "next_action":"review_inputs"}
# (a decline is a FINAL, billable answer — do not blind-retry the same body)Set state:"ca". Supply medicareWages + caWithholding alongside wages. Returns CA 540 lines with FTB primary-source cites.
curl https://tax-mcp.com/api/mcp/tax-compute/state \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"state":"ca","year":2025,"filingStatus":"single",
"wages":80000,"medicareWages":80000,"caWithholding":5000}'
# -> 200 {"computed":true, "stateAGI":80000, "stateTaxableIncome":74294, "totalTax":3196}Set state:"ny". NY fail-closes without nyWageAdditions (the NY-specific wage add-backs the engine can't infer). Add nycResident:true for the NYC resident tax.
curl https://tax-mcp.com/api/mcp/tax-compute/state \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"state":"ny","year":2025,"filingStatus":"single",
"wages":80000,"nyWageAdditions":0,"nycResident":true}'
# -> 200 {"computed":true, ...NY IT-201 (+ NYC resident) lines...}Only CA and NY/NYC are modeled today. Any other state is a 400 (a request-shape error, not a compute) — check GET /api/mcp/tax-capabilities before sending.
curl https://tax-mcp.com/api/mcp/tax-compute/state \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"state":"tx","year":2025,"filingStatus":"single","wages":80000}'
# -> 400 (Texas is not modeled — no wrong-but-plausible number is returned)Non-mutating. sourceProfile is what YOU assert (same shape as /state); filed is the return's own figures (display-only — never fed into the compute, §6694). The engine computes independently and diffs. A subset of filed lines → needs_review (the source set looks incomplete), never a false 'wrong'.
curl https://tax-mcp.com/api/mcp/tax-compute/reconcile \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"sourceProfile":{"state":"ca","year":2025,"filingStatus":"single",
"wages":80000,"medicareWages":80000,"caWithholding":5000},
"filed":{"stateAGI":95000}}'
# -> 200 {"status":"needs_review", "diff":[...DIFFERS on CA AGI: engine 80000 vs filed 95000...],
# "summary":{"DIFFERS":1,"NO-FILED":5}, "next_action":"review_inputs"}Federal-only. Send up to 25 complete profiles; get each result back INDEPENDENTLY — no deltas, no comparison, no ranking (any comparison is YOUR act). Bills PER SCENARIO (each computed/decline slot is one billable unit). Idempotency-Key is MANDATORY here (absent → 400) — unlike the single-compute endpoints.
curl https://tax-mcp.com/api/mcp/tax-compute/what-if \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"scenarios":[
{"label":"base","profile":{"year":2025,"filingStatus":"single","wages":90000}},
{"label":"with-bonus","profile":{"year":2025,"filingStatus":"single","wages":120000}}]}'
# -> 200 {"workflow":"what-if", "scenario_count":2, "results":[
# {"index":0,"label":"base","computed":true,"totalTax":11249},
# {"index":1,"label":"with-bonus","computed":true,"totalTax":17867}]}
# (iterate by index; a slot can independently decline; NO winner/delta field is returned)Federal-only. Recomputes up to 25 profiles fresh and returns a packet of N items, each with its OWN computed envelope / decline / provenance / legal_notice. Present-not-conclude: NO packet-level total, summary, count, or ranking — it is a non-exhaustive assembly for human review, NOT a completed or filed return. Bills per scenario; Idempotency-Key MANDATORY.
curl https://tax-mcp.com/api/mcp/tax-compute/review-packet \
-H "Authorization: Bearer taxmcp_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"scenarios":[
{"label":"single-90k","profile":{"year":2025,"filingStatus":"single","wages":90000}},
{"label":"mfj-180k","profile":{"year":2025,"filingStatus":"married_filing_jointly","wages":180000}}]}'
# -> 200 {"workflow":"review-packet", "items":[
# {"index":0,"label":"single-90k","computed":true,"totalTax":11249,"legal_notice":{...}},
# {"index":1,"label":"mfj-180k","computed":true,"totalTax":22498,"legal_notice":{...}}]}
# (each item is self-contained; there is deliberately NO cross-item total/summary)Before sending any taxpayer data, read the machine-readable supported envelope — years, jurisdictions, statuses, modeled fields, required affirmations, decline codes, and billing semantics. No auth, no quota charge.
curl https://tax-mcp.com/api/mcp/tax-capabilities
# -> 200 {"supported_envelope":{...}, "decline_codes":{...}, "affirmations":{...},
# "billing_semantics":{...}, "review_states":{...}, ...}These cover the full advertised surface (federal · CA/NY/NYC state · reconcile · what-if · review-packet · capability discovery). The authoritative, always-current field lists, affirmations, and decline codes are published at GET /api/mcp/tax-capabilities and the OpenAPI spec at /api/openapi.json — the docs and the manifest never drift.
Send an Idempotency-Key header (a unique value per logical request) so a retry never double-bills. Retrying with the same key + same body replays the byte-identical first response (with header Idempotent-Replayed: true) and bills exactly once. The same key with a different body returns 409 idempotency_key_reused; a concurrent in-flight duplicate returns 409 idempotency_key_in_progress (retry). Keys are scoped to your account and replay for 24h. Errors are never cached — a retry after a 4xx/5xx is a fresh request.
curl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"year":2025,"filingStatus":"single","wages":120000}'A successful compute (200) returns either a computed return or an honest decline— both are billable answers. Every dollar amount is whole-dollar per the form's rounding, and each line carries its own primary-source derivation.
The computed and deterministic-decline examples below preserve captured live response shapes from https://tax-mcp.com (long line/derivation arrays are elided with a marker for brevity; run the call to see them in full). The needs_review reconcile example is sanitized to show the same contract fields without publishing taxpayer-like line values. Note on billed: it reflects the calling key's plan — these were captured with an internal test key (billed: false); on a customer plan a computed or declined 200 bills per the published billing_semantics (see the state table below).
// COMPUTED 200 — exact live response
// request: {"year":2025,"filingStatus":"single","wages":85000,"medicareWages":85000,"federalWithholding":9500}
{
"computed": true,
"year": 2025,
"filingStatus": "single",
"taxableIncome": 69250,
"incomeTax": 10149,
"totalTax": 10149,
"lines": "<3 whole-dollar line items, each with a per-line primary-source derivation \u2014 elided for brevity; run the call to see them in full>",
"flags": [
"OBBBA-2025-new-deductions-senior-bonus/tips/overtime/car-loan-NOT-evaluated-engine-cannot-verify-eligibility-from-W2/1099-preparer-must-confirm"
],
"owedOrRefund": 649,
"billed": false,
"retry_safe": true,
"next_action": "none",
"provenance": {
"engine": "deterministic",
"jurisdiction": "federal",
"tax_year": 2025
},
"legal_notice": {
"ref": "https://tax-mcp.com/terms#compute-notice",
"version": "v1",
"text": "Computation support only. Not tax, legal, accounting, or financial advice. Customer is responsible for review, filing positions, and use only within the documented supported envelope."
}
}// DETERMINISTIC DECLINE 200 — a real captured response, shown IN FULL (no elision).
// This is the exact response to the 'Try a decline' request below.
{
"computed": false,
"code": "schedule_c_needs_review",
"reason": "Schedule C self-employment compute requires scheduleCIsSingleSolePropFinalNetAffirmed:true \u2014 attesting the amount is the FINAL Schedule C line-31 net of ONE sole proprietor with no depreciation/\u00a7179/bonus, no SE health-insurance (\u00a7162(l)) or home-office (\u00a7280A), not farm/clergy/statutory-employee, not a \u00a71402 optional method, and not two spouses' combined self-employment (the Social-Security OASDI cap is per-individual \u2014 combining two spouses' SE into one scalar under-collects). Any of those \u2192 a preparer must complete Schedule C + Schedule SE. (The \u00a7199A QBI 20% deduction is a separate opt-in, scheduleCQbiEligible.)",
"billed": false,
"retry_safe": false,
"next_action": "review_inputs"
}The engine also has a first-class needs_review state on the reconcile surface — when the engine computes independently and finds a filed-return comparison or source set that needs human adjudication. The source profile is caller-supplied; filed values are reconciliation oracles, not compute inputs. The engine returns needs_reviewwhen the source set looks incomplete and never declares either figure “wrong”:
// NEEDS_REVIEW 200 (reconcile) — sanitized response shape; tax values elided
// request shape: {"sourceProfile":{"state":"ca","year":2025,"filingStatus":"single",...},
// "filed":{"stateAGI":"<caller-supplied filed value>"}}
{
"status": "needs_review",
"computed": {
"computed": true,
"state": "ca",
"year": 2025,
"stateTaxableIncome": "<computed value elided>",
"totalTax": "<computed value elided>",
"lines": "<CA 540 line items with FTB primary-source cites elided>"
},
"filed": {
"stateAGI": "<filed value supplied by caller, elided>"
},
"diff": [
{
"line": "CA AGI (540:17)",
"engine": "<computed value elided>",
"filed": "<filed value elided>",
"delta": "<difference elided>",
"cls": "DIFFERS",
"note": "source set appears INCOMPLETE vs the filed oracle \u2014 a human reviews; the engine never assigns blame"
}
],
"summary": {
"MATCH": 0,
"ENGINE-DECLINED": 0,
"DIFFERS": 1,
"NO-FILED": 5
},
"docTriage": [],
"billed": false,
"retry_safe": true,
"next_action": "review_inputs"
}The decline code names the specific surface a preparer must handle — e.g. year_not_modeled, out_of_scope_income, surtax_additional_medicare, capital_gain_lots_need_review, schedule_c_needs_review, schedule_e_needs_review, digital_asset_needs_review, and others. The authoritative, always-current set is published at GET /api/mcp/tax-capabilities (decline_codes.federal) and in the OpenAPI DeclineCode enum. A decline is never a wrong-but-plausible number — it tells you the case is outside the modeled surface.
The complete state contract, tied to the live retry_safe / next_action enums (the exact strings the API returns) and the published billing_semantics. Glosses in parentheses.
| State | HTTP | Bills? | retry_safe | next_action (gloss) — what YOU do next | What a client may show its user |
|---|---|---|---|---|---|
| Computed | 200 | Yes (customer plans) | true | none (done — the answer is final) | The computed figures + the per-line derivation (primary-source citations) |
| Deterministic decline | 200 | Yes (customer plans) | false | review_inputs (same body re-declines; a preparer handles the named surface) | “This case needs preparer review” + the named reason (a safe, final answer — not an error) |
| needs_review (reconcile) | 200 | Yes (customer plans) | true | review_inputs (the reconcile ran; a human resolves the flagged items) | The reconcile diff + the flagged items for a human to resolve |
| Validation failure | 400 | No | false | fix_request_and_resubmit (correct the body) | A request-error state (nothing computed, nothing billed) — fix and resend |
| Auth failure | 401/403 | No | false | check_credentials (fix or replace the key) | A sign-in / API-key problem |
| Monthly quota exhausted | 429 | No | false | upgrade_plan_or_wait_next_period (hard cap — no overage billing) | “Monthly limit reached” — upgrade or wait for the next period |
| Per-minute rate exceeded | 429 | No | true | retry_after_backoff (back off and retry) | A transient “busy, retrying” state (retry succeeds shortly) |
| Idempotent replay (same key + body) | 200 | Once total | — | none needed (byte-identical replay; header Idempotent-Replayed: true) | The same result as the first call — reassure “no double charge” |
| Idempotency key reused (different body) | 409 | No | false | use_new_idempotency_key | A client bug (a key was reused for a changed request) — mint a fresh key |
| Idempotency in progress (concurrent dup) | 409 | No | true | retry_after_inflight_completes (wait, retry the same key) | A transient “still processing” state — retry the same key shortly |
| Compute error | 500 | No | false | fix_request_and_resubmit (report it — a 500 is our bug) | A generic “something went wrong” (nothing billed; it’s our bug, not the caller’s) |
| Metering outage | 503 | No | true | retry_after_backoff (never silently served free) | A transient “temporarily unavailable” state — retry (never silently billed) |
A hosted ChatGPT/Claude one-click connector is not yet generally available. What exists today is self-configured integration: you bring your own API key and wire the connection yourself. Two supported paths:
Steps as of July 2026 — the GPT-builder UI evolves, so cross-check OpenAI's Actions docs if a step looks different:
https://tax-mcp.com/api/openapi.json. The spec is OpenAPI 3.1 — if your importer rejects 3.1, the supported fields are also listed (machine-readable) at tax-capabilities.Authorization: Bearer scheme, so the importer should prompt for the key. Use a dedicated, revocable key for assistant integrations (create one in your dashboard) — you can revoke it without touching your production key. (Note: quota is per-account, not per-key — a leaked key can consume your account's monthly quota until revoked.)Calls made through a GPT Action hit the normal metered REST API — the same billing, quota, idempotency, and 429 semantics as any other client (response states).
Tax MCP exposes a bounded machine MCP transport at POST https://tax-mcp.com/mcp (Streamable HTTP) with three tools: tax_capabilities, tax_compute_federal, tax_compute_state. It authenticates with the same Authorization: Bearer key, checked before the MCP handshake.
Authorization: Bearer YOUR_MCP_API_KEY. (A browser claude.ai "custom connector" flow that requires OAuth will not work against this static-key endpoint.)computed:false + a reason), not protocol errors — an assistant should read the result, not retry it./mcp transport is currently an eval (non-metered) posture; subject to change — unlike the REST API, it does not meter usage or support idempotency keys today.Safety: Do not paste taxpayer facts into a generic chatbot unless your integration is configured to call Tax MCP through an authenticated, logged, policy-compliant tool path — anything you type into an assistant is processed by that assistant's provider before a tool call ever reaches Tax MCP.
Every computed:true compute 200 and every reconcile 200 (regardless of status) carries a legal_notice object (ref / version / text); it is absent only from 200 declines (computed:false) and non-200 errors. That absence never means the Terms don't apply: /terms governs every response — declines and errors included — and legal_notice is simply a per-success machine-readable echo of it. The output is computation support only — not tax, legal, accounting, or financial advice, and not a filing. A §6694 preparer relying on a computed line remains responsible for the return; the per-line derivation cites the primary source (IRC / Rev. Proc. / FTB schedule) precisely so that responsibility is auditable. Binding terms live at /terms.
The point of this API is that it refuses to guess. This request looks computable — a W-2 wage-earner with $20,000 of Schedule C income — but it omits the required §6694 affirmation (scheduleCIsSingleSolePropFinalNetAffirmed) that the amount is a single sole-proprietor final net with no QBI / depreciation / spouse-split, so the engine returns a deterministic 200 decline instead of a plausible-but-wrong number. (Add the affirmation — and socialSecurityWages when W-2 wages are present — and it computes the self-employment tax.) Paste it with your key and you get the exact schedule_c_needs_review body above, every time:
curl https://tax-mcp.com/api/mcp/tax-compute/federal \
-H "Authorization: Bearer taxmcp_YOUR_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"year":2025,"filingStatus":"single","wages":80000,"scheduleCIncome":20000}'
# -> HTTP 200 { "computed": false, "code": "schedule_c_needs_review", "next_action": "review_inputs", ... }Same idea for other boundaries: an unmodeled year (e.g. 2019) → year_not_modeled; a §1250 / collectibles special-rate gain → special_rate_gain_unmodeled. Discover the whole envelope up front (no taxpayer payload needed) at GET /api/mcp/tax-capabilities.
The full product roadmap is published at /roadmap— what is modeled now, what is certifying, and what is planned — so you can see what is next and influence the order. Live today: the federal 1040, CA / NY / NYC state, and reconcile surfaces documented above, plus the what-if and review-packet workflows. Binding Terms and the per-response legal_notice (v1) are live and govern every response. The next major surface on the roadmap is document-upload → structured-JSON-envelope extraction (source-intake only; nothing is promoted to a compute input without your assertion).
Successful responses carry your current usage so you can back off before hitting a limit. Both quota (monthly, account-wide) and rate (per-minute) fail closed with 429 — never a silent overage charge.
| Header | Meaning |
|---|---|
| X-Quota-Limit / X-Quota-Remaining | Monthly call allowance and calls left (shared across your keys) |
| X-RateLimit-Limit / X-RateLimit-Remaining | Per-minute request allowance and requests left this minute |
| Idempotent-Replayed | true when this response was replayed from a prior Idempotency-Key |
400 — invalid body or failed validation401 — missing / unknown / revoked key403 — account suspended (the key is valid — contact billing)409 — idempotency conflict (idempotency_key_reused = same key, different body; idempotency_key_in_progress = concurrent duplicate, retry)429 — monthly quota exhausted (account-wide) or per-minute rate exceeded500 — server error computing the return503 — metering outage (the call is never silently served free)Full machine-readable spec: /api/openapi.json (OpenAPI 3.1).