Metadata-Version: 2.4
Name: factora
Version: 0.1.2
Summary: Official Python SDK for Factora E-Invoicing API (XRechnung & ZUGFeRD / EN 16931)
Project-URL: Homepage, https://factora.software
Project-URL: Documentation, https://console.factora.software/docs
Project-URL: Repository, https://github.com/factora-software/factora-python
Author: Factora
License: MIT License
        
        Copyright (c) 2026 Factora
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: e-invoicing,en16931,invoice,peppol,xrechnung,zugferd
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.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Accounting
Requires-Python: >=3.9
Requires-Dist: requests>=2.28.0
Description-Content-Type: text/markdown

# factora-python

[![PyPI version](https://img.shields.io/pypi/v/factora.svg?v=2)](https://pypi.org/project/factora/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Official Python SDK for the Factora E-Invoicing API — create legally compliant
German and European electronic invoices (**XRechnung** and **ZUGFeRD**, both
based on **EN 16931**) from a single HTTP call.

## Installation

```bash
pip install factora
```

Requires Python 3.9+.

## Quickstart

An invoice is created with one POST to `/api/v1/invoices/atomic/`. The SDK wraps
that endpoint in `FactoraClient.create_atomic_invoice()`.

```python
import uuid

from factora import FactoraAPIError, FactoraClient

client = FactoraClient(api_key="fa_live_your_api_key")

payload = {
    "api_mode": "atomic_single_post",
    "invoice_header": {
        "invoice_number": "RE-2026-0001",
        "invoice_date": "2026-08-06",
        "currency": "EUR",
        "invoice_type": "380",
        "buyer_reference": "04011000-12345-34",  # Leitweg-ID, required by BR-DE
        "profile": "xrechnung",  # or "en16931"; omit to get "xrechnung"
    },
    "seller_snapshot": {
        "name": "Muster GmbH",
        "street": "Musterstrasse 1",
        "zip": "10115",
        "city": "Berlin",
        "country": "DE",
        "vat_id": "DE123456789",
        "contact": {
            "name": "Alex Muster",
            "phone": "+49 30 1234567",
            "email": "billing@muster-gmbh.example",
        },
    },
    "buyer": {
        "name": "Beispiel AG",
        "street": "Beispielweg 7",
        "zip": "80331",
        "city": "Muenchen",
        "country": "DE",
        "vat_id": "DE987654321",  # or buyer["contact"]["email"]
    },
    "items": [
        {
            "description": "Consulting services, August 2026",
            "quantity": "2",
            "unit": "H87",  # UN/ECE Rec. 20 code, H87 = piece
            "unit_price_net": "750.00",
            "vat_rate": "19.00",
        }
    ],
    "validation_totals": {
        "net_amount": "1500.00",
        "vat_amount": "285.00",
        "gross_amount": "1785.00",
    },
}

try:
    result = client.create_atomic_invoice(
        payload,
        idempotency_key=str(uuid.uuid4()),
    )
except FactoraAPIError as exc:
    print(f"HTTP {exc.status_code}: {exc}")
    print(exc.response_data)  # full response envelope, incl. errors[]
else:
    print(result["valid"], result["data"])
```

`FactoraClient` is also a context manager, which closes the underlying HTTP
session for you:

```python
with FactoraClient(api_key="fa_live_your_api_key") as client:
    result = client.create_atomic_invoice(payload)
```

### Idempotency

Pass `idempotency_key` with a value you generate (a UUID works well). Replaying
the same key returns the original result instead of creating a second invoice —
safe to use for retries after a timeout.

## Request essentials

The API validates strictly; a rejected request comes back as HTTP 400 with a
list of errors rather than a partially created invoice.

**Top level**

| Field | Required | Notes |
| --- | --- | --- |
| `api_mode` | yes | Must be `"atomic_single_post"` |
| `invoice_header` | yes | See below |
| `validation_totals` | yes | `net_amount`, `vat_amount`, `gross_amount` |
| `seller_snapshot` **or** `mandant_id` | yes | Exactly one — sending both or neither returns 400 |
| `buyer` | yes | See below |
| `items` | yes | At least one line item |

**`invoice_header`** — `invoice_number` and `invoice_date` are required;
`currency` defaults to `"EUR"` and `invoice_type` to `"380"` (commercial
invoice). `profile` also lives here: `"xrechnung"` (default) or `"en16931"`.
It is **not** a top-level field — sent at the top level it is ignored without
an error and the invoice silently falls back to `"xrechnung"`. The resolved
profile is always returned in the response.

**`seller_snapshot`** — `name` is required.

**`buyer`** — `name` is required.

**`items[]`** — each line needs `description`, `quantity`, `unit`
(UN/ECE Rec. 20 code, e.g. `"H87"`), `unit_price_net` and `vat_rate`.

### Additional rules under `invoice_header.profile: "xrechnung"`

The German CIUS (BR-DE rules) requires these on top of the base fields:

- `invoice_header.buyer_reference` — the Leitweg-ID
- `seller_snapshot.city` and `seller_snapshot.zip`
- `seller_snapshot.contact.phone` and `seller_snapshot.contact.email`
- `buyer.city` and `buyer.zip`
- an electronic address for the buyer — either `buyer.vat_id` with a `DE`
  prefix, or `buyer.contact.email`

## Response envelope

Every call returns the same envelope:

```json
{
  "valid": true,
  "data": { "...": "created invoice" },
  "errors": [],
  "meta": { "...": "request metadata" }
}
```

Error entries always carry `code`, `severity` and `message`, and may
additionally include `field`, `rule`, `bt` (the EN 16931 business term) and
`location`.

## Error handling

Any non-2xx response, and any transport failure, raises `FactoraAPIError`:

| Attribute | Description |
| --- | --- |
| `status_code` | HTTP status code, or `None` if the request never reached the server |
| `response_data` | Decoded response envelope, or `None` when the body was absent or not JSON |

```python
try:
    client.create_atomic_invoice(payload)
except FactoraAPIError as exc:
    if exc.status_code == 400 and exc.response_data:
        for error in exc.response_data.get("errors", []):
            print(error["code"], error["severity"], error["message"])
    else:
        raise
```

## Configuration

```python
FactoraClient(
    api_key="fa_live_your_api_key",
    base_url="https://console.factora.software",  # default
    timeout=30,                                    # seconds, default
)
```

API keys are issued in the Factora Console. Keys prefixed `fa_test_` target the
sandbox, `fa_live_` target production. Keep them out of source control — read
them from an environment variable or a secrets manager.

## Documentation

Full API reference: <https://console.factora.software/docs>

## License

MIT — see [LICENSE](LICENSE).
