Metadata-Version: 2.4
Name: agenttax
Version: 0.3.0
Summary: Python SDK for the AgentTax API — sales tax, use tax, and capital gains for AI agents
License: MIT
Project-URL: Homepage, https://agenttax.io
Project-URL: Documentation, https://agenttax.io/api-docs
Project-URL: Repository, https://github.com/AgentTax/AgentTax
Project-URL: Bug Tracker, https://github.com/AgentTax/AgentTax/issues
Project-URL: Agent Guide, https://agenttax.io/api/v1/agents
Project-URL: Pricing, https://agenttax.io/api/v1/pricing
Keywords: tax,sales-tax,ai-agent,agentic,x402,capital-gains,compliance,fintech
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: requests-mock>=1.11; extra == "dev"

# AgentTax Python SDK

Sales tax, use tax, and capital gains for AI agents. Full docs at [agenttax.io/api-docs](https://agenttax.io/api-docs).

## Install

```bash
pip install agenttax
```

## Quickstart — no signup required

```python
from agenttax import AgentTaxClient

client = AgentTaxClient()          # keyless: demo mode, 10 calls/day

result = client.calculate(
    role="buyer",                  # buyer side needs no nexus setup
    amount=1000.00,
    buyer_state="TX",
    transaction_type="compute",
    counterparty_id="agent_seller_001",
)

print(result["total_tax"])         # use tax owed on the purchase
print(result["combined_rate"])     # the location's composed rate (state + local)
print(result["applied_rate"])      # the rate actually applied to compute total_tax
print(result["confidence"])        # {"score": ..., "level": ...}
```

Demo mode returns the tax figure, the rate and a confidence score. It omits the
audit trail, jurisdiction breakdown and advisories, and it keeps no history.
The response also carries `resolution` (a resolved, partial or unknown status for location, nexus and taxability) and `intent`, which is always `"estimate"` on a keyless call.

## API Key

Get a free API key at [agenttax.io](https://agenttax.io) — 1,500 calls/month at no
cost — for the full response plus transaction history, nexus monitoring, capital
gains tracking and 1099 export.

```python
client = AgentTaxClient(api_key="atx_live_...")
```

### Why `role="seller"` returns $0.00 on a new account

A seller only has to collect tax where it has economic nexus, and a new account
has none configured — so every seller-side calculation correctly returns
`total_tax` 0 with `final_determination: "no_nexus"` until you set nexus up:

```python
import requests
requests.post(
    "https://agenttax.io/api/v1/nexus",
    headers={"X-API-Key": "atx_live_..."},
    json={"nexus": {"TX": {"hasNexus": True, "reason": "Economic nexus"}}},
)
```

Or pass `nexus_override={"TX": {"hasNexus": True}}` to `calculate()` to model a
seller position without saving it. Note the shape — a bare list of state codes
is read as "no nexus" and silently returns $0.00. Buyer-side calls need none of
this, which is why the quickstart above starts there.

## Methods

### `calculate(**kwargs)`

Calculate sales tax for a transaction.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `role` | str | ✓ | `"seller"` or `"buyer"` |
| `amount` | float | ✓ | Transaction amount in USD |
| `buyer_state` | str | ✓ | Two-letter state code |
| `transaction_type` | str | ✓ | `"saas"`, `"compute"`, `"consulting"`, etc. |
| `counterparty_id` | str | ✓ | Counterparty identifier |
| `work_type` | str | | What the work is. One of `"compute"`, `"research"`, `"information_service"`, `"content"`, `"consulting"`, `"trading"`, `"payment_processing"`, `"web_hosting"`, `"software_maintenance"`, `"it_support"`, `"custom_software"` |
| `is_b2b` | bool | | Business-to-business transaction |
| `use_context` | str | | `"enterprise_system"`, `"individual"`, or `"mixed"` — Maryland-only in v1, declares whether the purchase is solely for commercial use in an enterprise computer system (TB-56); absent = unchanged `is_b2b`-proxy default |
| `buyer_zip` | str | | 5-digit ZIP for local rate lookup |
| `seller_remitting` | bool | | Buyer mode: the seller already collected the tax |
| `nexus_override` | dict | | Model nexus for this one call, e.g. `{"TX": {"hasNexus": True}}` |
| `idempotency_key` | str | | Sent as the `Idempotency-Key` header; a retry with the same key and body replays the first response for 24 hours |
| `intent` | str | | `"estimate"` or `"record"` (since 0.3.0) — see below |
| `seller_state` | str | | Seller's two-letter state code (since 0.3.0) |
| `seller_zip` | str | | Seller's 5-digit ZIP. Required for TX, UT, AZ and TN origin-sourced intrastate sales, and for intrastate OH `license` or `digital_good` sales (since 0.3.0) |
| `marketplace_role` | str | | `"seller"`, `"facilitator"` or `"direct"` (API default `"direct"`) (since 0.3.0) |
| `exemption_certificate` | str | | Exemption certificate reference, up to 200 characters (since 0.3.0) |
| `description` | str | | Free-text description, up to 1000 characters, recorded with the transaction (since 0.3.0) |
| `digital_content_type` | str | | With `transaction_type="digital_good"` only. What the download is: `"ebook"`, `"audio"`, `"video"`, `"art_image"`, `"photograph"`, `"printable_document"`, `"software"`, `"data"` or `"other"`. Absent = the generic digital_good answer (since 0.3.0) |
| `payment_provider_type` | str | | With `transaction_type="payment_processing_fee"`. Who provides the processing: `"bank"`, `"licensed_money_transmitter"`, `"card_network"`, `"processor_routing_to_bank_or_network"` or `"other"`. Absent = the conservative answer (since 0.3.0) |
| `it_support_scope` | str | | With `it_support`. What the engagement is: `"help_desk_advice"`, `"remote_repair_or_config"`, `"software_installation"`, `"monitoring"`, `"security_monitoring"` or `"managed_bundle"`. Absent = a managed bundle (since 0.3.0) |
| `includes_prewritten_software_access` | bool | | With `transaction_type="web_hosting"`. `True` when the plan includes vendor software beyond a control panel or file manager. Absent or `False` = plain hosting (since 0.3.0) |
| `custom_software_attestation` | dict | | Required with `work_type="custom_software"`. Without a complete attestation the call is answered as prewritten software or SaaS. See below (since 0.3.0) |
| `maintenance_contract` | dict | | With `transaction_type="software_maintenance"`. Facts about the contract. Every key is optional and a missing fact is answered conservatively. See below (since 0.3.0) |

#### Estimate without saving (since 0.3.0)

Pass `intent="estimate"` on a keyed call to price a purchase before you commit
to it. You get the same figures, and nothing is saved: no transaction row, no
nexus revenue, and the `Idempotency-Key` header is ignored (a paid retry is a
new paid call). An estimate still counts toward your monthly calls. `"record"`
is the default on keyed calls; keyless calls are always estimates.

```python
quote = client.calculate(
    role="buyer",
    amount=1000.00,
    buyer_state="TX",
    transaction_type="compute",
    counterparty_id="agent_seller_001",
    intent="estimate",
)

print(quote["intent"])                            # "estimate"
print(quote["resolution"]["taxability"]["status"])  # "resolved" | "partial" | "unknown"
```

Every response carries `resolution`: a typed status (`resolved`, `partial` or
`unknown`) and a basis for each of location, nexus and taxability.
`total_tax` is never withheld, so if you want "unknown means don't charge",
branch on `resolution[...]["status"]` yourself.

#### Classification facts (since 0.3.0)

Some products are taxed differently depending on facts only you know. Send
them and the API uses them. Leave them out and you get the conservative
answer. The SDK checks no values. The API does, and a bad value raises
`ValidationError`. Dict facts are sent unchanged, so their inner keys use the
API's snake_case names. Each fact is your representation, recorded with the
transaction. It is not a state safe harbor.

```python
# A printable planner sold as a download
client.calculate(
    role="seller",
    amount=12.00,
    buyer_state="NY",
    transaction_type="digital_good",
    counterparty_id="buyer_001",
    digital_content_type="printable_document",
)

# Custom software written for one buyer
client.calculate(
    role="seller",
    amount=5000.00,
    buyer_state="TX",
    transaction_type="service",
    work_type="custom_software",
    counterparty_id="buyer_002",
    custom_software_attestation={
        "written_for_buyer": True,
        "not_offered_to_others": True,
        "deliverable": "source_code",           # or "compiled_artifact"; "hosted" is SaaS
        "is_modification_of_prewritten": False,
    },
)

# A software maintenance contract with itemized parts
client.calculate(
    role="seller",
    amount=1000.00,
    buyer_state="PA",
    transaction_type="software_maintenance",
    counterparty_id="buyer_003",
    maintenance_contract={
        "includes_upgrades": True,
        "includes_support": True,
        "separately_itemized": True,
        "itemized_amounts": {"updates": 600, "support": 400},  # sums to amount
    },
)
```

The optional state facts inside `custom_software_attestation` and
`maintenance_contract` are listed at
[agenttax.io/api-docs](https://agenttax.io/api-docs).

### `get_rates(state=None, format=None, explain=False)`

Fetch sales tax rates for all 51 jurisdictions. No auth required.

### `get_capital_gains_rates(state=None)`

Fetch state capital gains rates. No auth required.

### `get_local_rate(zip_code)`

Fetch combined local + state rate for a ZIP code.

### `get_transactions(limit=50, buyer_state=..., role=..., since=...)`

Fetch transaction history — the newest `limit` rows (max 1000). Filters:
`buyer_state`, `role`, `since` (ISO date), passed as keywords. Auth required. The endpoint has no
offset or transaction-type filter: passing `offset` or `transaction_type`
raises `ValidationError` rather than being silently ignored. Page with `since`.

### `log_trade(**kwargs)` / `get_trades(**kwargs)`

Capital gains trade tracking (FIFO/LIFO/Specific ID). Auth required.
`log_trade` takes `fee_amount`, `accounting_method` (`"fifo"`, `"lifo"`,
`"specific_id"`), `specific_lot_id` (required with `"specific_id"`) and
`resident_state` (the taxpayer's state of residence, for the state estimate).
The 0.1.0 names `fees`, `cost_basis_method` and `buyer_state` are still
accepted and sent under the canonical names.

## Error Handling

```python
from agenttax import AgentTaxClient, AuthError, RateLimitError, ValidationError

client = AgentTaxClient(api_key="atx_live_...")

try:
    result = client.calculate(...)
except AuthError:
    # Invalid or missing API key
    pass
except RateLimitError:
    # Monthly quota exceeded — upgrade tier or wait
    pass
except ValidationError as e:
    # Bad request parameters
    print(e)
```

## Free Tier

1,500 API calls/month at $0. Paid plans start at $25/month (10K calls).
See [agenttax.io/pricing](https://agenttax.io/pricing).
