# Python SDK

Install the fiscalrail package and use its typed, pooled API client.

The official `fiscalrail` package is the recommended way to call FiscalRail from Python. It provides typed request parameters, immutable response dataclasses, pooled connections, safe retries and country-specific tax helpers without adding a runtime validation framework.

## Install and authenticate

```bash
python -m pip install fiscalrail
export FISCALRAIL_API_KEY=ak_test_...
```

Create one client and reuse it for the lifetime of your application process:

```python
import os

from fiscalrail import FiscalRail

client = FiscalRail(os.environ["FISCALRAIL_API_KEY"])
```

The API key is a required constructor argument. Your application decides whether it comes from an environment variable, secret manager or another configuration source; the SDK does not inspect process environment variables. Test and Live use the same base URL, and the key selects the account environment.

`FiscalRail` owns a pooled `requests.Session`. Use it as a context manager in short-lived scripts, or call `client.close()` during application shutdown. You can inject your own `requests.Session` when you need custom proxies, TLS settings, adapters or observability; injected sessions remain owned by your application.

## Issue an invoice

Use `Decimal` for monetary input and the Spain helpers for catalog-backed taxes:

```python
import os
from decimal import Decimal

from fiscalrail import FiscalRail
from fiscalrail.tax_regimes.es import irpf, vat

client = FiscalRail(os.environ["FISCALRAIL_API_KEY"])
invoice = client.invoices.issue(
    customer="cus_...",
    lines=[
        {
            "description": "Consulting services",
            "unit_price": Decimal("2500.00"),
            "taxes": [vat.general, irpf.professionals],
        }
    ],
)

print(invoice.code)
print(invoice.totals.payable)
```

Invoice issuance and amendment generate an idempotency key automatically. Durable jobs should supply and persist their own `idempotency_key` so a retry after a process restart identifies the same intended operation.

## Typed requests and responses

Methods accept typed keyword arguments directly. Exported `TypedDict` definitions are useful when building a payload before making the call:

```python
from decimal import Decimal

from fiscalrail.params import InvoiceIssueParams
from fiscalrail.tax_regimes.es import vat

params = InvoiceIssueParams(
    customer="cus_...",
    lines=[
        {
            "description": "Consulting services",
            "unit_price": Decimal("2500.00"),
            "taxes": [vat.general],
        }
    ],
)

invoice = client.invoices.issue(**params)
```

Responses are frozen dataclasses generated from FiscalRail's OpenAPI contract. Dates become `date`, timestamps become `datetime`, and decimal strings become `Decimal`. Unknown response fields remain available through `response.extra_fields` so adding a response field does not break older SDK versions.

## Retries and errors

The client retries connection failures, timeouts, `408`, `429` and transient `5xx` responses only when the operation is safe to retry. It honors `Retry-After` and otherwise uses capped exponential backoff. The default is two retries; configure `max_retries=0` to disable them.

FiscalRail errors are raised as subclasses of `FiscalRailError`. API errors expose `status_code`, `code`, `request_id` and validation details where available. Connection and timeout errors preserve the idempotency key so a durable worker can decide how to resume.

## Available resources

- `client.accounts`
- `client.api_keys`
- `client.customers`
- `client.event_destinations`
- `client.events`
- `client.invoice_series`
- `client.invoices`
- `client.invoice_pdfs`
- `client.tax_ids`
- `client.tax_regimes`

Invoices use the domain verbs `issue` and `amend`; issued documents are never updated. Every operation in the [API reference](/en/api/introduction) has a corresponding SDK resource method.

## Verify webhooks

Pass the unmodified request body, the `FiscalRail-Signature` header and the destination's signing secret to `construct_event`:

```python
from fiscalrail.webhooks import construct_event

event = construct_event(raw_body, signature_header, signing_secret)
```

The helper verifies the HMAC in constant time, rejects timestamps older than five minutes and only then parses the JSON event. A failed check raises `WebhookSignatureError`.
