Skip to main content

vendus

PyPI version Python versions CI License: MIT Typed Docs

vendus is an unofficial, fully-typed Python SDK for the Vendus invoicing & POS API — Portugal's AT-certified faturação platform (also Spain). Issue invoices (FT), simplified invoices (FS), invoice-receipts (FR), receipts (RG) and credit notes (NC) against the Vendus API in a few lines of Python, with matching sync and async clients.

Vendus handles all communication with the AT (Autoridade Tributária) — SAF-T, ATCUD, document hash and QR code come back ready on every document. This SDK talks to Vendus; Vendus talks to the AT.

📖 Documentation: https://vendus.bilouro.com/ · English
📖 Documentation: https://vendus.bilouro.com/pt/ · Português

Examples · API Reference

Community SDK — not affiliated with or endorsed by Vendus. For official integrations, visit vendus.pt.

Installation

pip install vendus      # or: uv add vendus

Quick Start

from decimal import Decimal
from vendus import ClientData, DocumentItem, TaxCategory, VendusClient

client = VendusClient(api_key="your-api-key")

# Issue an invoice (FT)
invoice = client.documents.create_invoice(
    register_id=1,
    client=ClientData(name="Acme Lda", fiscal_id="123456789"),
    items=[
        DocumentItem(
            description="Consulting hours",
            quantity=Decimal("10"),
            unit_price=Decimal("75.00"),  # gross (includes tax)
            tax_category=TaxCategory.NORMAL,
        ),
    ],
    external_reference="ORD-2026-001",   # enables safe POST retries
)

print(invoice.number)   # "FT 2026/123"
print(invoice.atcud)    # AT communication code
print(invoice.qrcode)   # AT QR code payload

Async Support

Every method has an async variant — same client, _async suffix:

invoice = await client.documents.create_invoice_async(
    register_id=1,
    client=ClientData(name="Acme Lda", fiscal_id="123456789"),
    items=[...],
)

Three client shapes (one API)

The same create_invoice / create_invoice_receipt handles all three cases:

# 1. Client with NIF (typical B2B)
client.documents.create_invoice(
    register_id=1, items=[...],
    client=ClientData(name="Acme Lda", fiscal_id="123456789"),
)

# 2. Client without NIF (B2C, customer gave name only)
client.documents.create_invoice(
    register_id=1, items=[...],
    client=ClientData(name="João Silva"),
)

# 3. Final consumer (anonymous, no identification at all)
client.documents.create_invoice(register_id=1, items=[...])

Do NOT pass fiscal_id="999999990" — the SDK rejects it. For final consumer, omit client.

Credit Notes

A credit note (NC) credits a previously issued invoice. It is also the only way to reverse a fiscal invoice — FT/FR cannot be cancelled. The SDK fetches the original and credits its full set of lines, so you pass only the id and a reason:

credit_note = client.documents.create_credit_note(
    reference_document_id=invoice.id,
    reason="Customer return",
    external_reference="REFUND-2026-001",
)

Error Handling

All errors inherit from VendusError with typed subclasses:

from vendus import (
    VendusClient,
    ValidationError,
    AuthenticationError,
    AuthorizationError,
    NotFoundError,
    RateLimitError,
    APIError,
    TransportError,
)

try:
    invoice = client.documents.create_invoice(...)
except ValidationError as e:
    # Local validation — invalid NIF, missing items, forbidden 999999990
    print(e)
except AuthenticationError:
    # API key rejected (401)
    ...
except RateLimitError:
    # 429 — back off
    ...
except APIError as e:
    # Other Vendus errors — inspect e.status_code and e.response_body
    ...
except TransportError:
    # Network failure — timeout, DNS, connection refused
    ...

Configuration

client = VendusClient(
    api_key="your-api-key",
    base_url="https://www.vendus.pt/ws",  # production (default)
    timeout=30.0,                          # seconds
    max_retries=3,                         # GET retries; POST only if external_reference present
)

# Or load from VENDUS_API_KEY
client = VendusClient.from_env()

Supported Documents

Document Code Method Status
Fatura FT client.documents.create_invoice ✅
Fatura Simplificada FS client.documents.create_simplified_invoice ✅
Fatura-Recibo FR client.documents.create_invoice_receipt ✅
Recibo RG client.documents.create_receipt ✅
Nota de Crédito NC client.documents.create_credit_note ✅
Orçamento OT — roadmap
Guia de Transporte GT — roadmap
Nota de Débito ND — roadmap

Validation status

The wire format of every operation is asserted by unit tests (respx mocks), and validated against the real Vendus API — in test mode (mode=tests, non-fiscal) where possible, and once in real mode for the operations that test mode can't reach:

Operation Unit Live
create_invoice (FT) ✅ ✅ test + real
create_simplified_invoice (FS) ✅ ✅ test + real (credited by NC)
create_invoice_receipt (FR) ✅ ✅ test + real (+ payment variations)
create_receipt (RG) ✅ ✅ test + real (references an invoice)
create_credit_note (NC) ✅ ✅ real (full + partial; credits FT/FR/FS)
cancel ✅ ✅ refuses FT/FR/NC; cancels a receipt (RG)
list_payment_methods / list_registers / list / get ✅ ✅ read-only

Test-mode documents ("Modo de Formação") are non-fiscal and never reported to the AT, but Vendus stores them in a separate space — they can't be retrieved or credited via /documents/{id}, so credit notes are validated in real mode. Fiscal invoices (FT/FR/NC) can't be cancelled (reverse them with a credit note); a receipt (RG) can — both paths are live-verified.

Why This SDK

  • Fully typed — mypy --strict passes, py.typed marker included. Full autocomplete in VS Code and PyCharm.
  • Sync + Async — one client, no separate packages. httpx powers both.
  • Decimal amounts — no floating-point surprises with money. Decimal("49.90"), not 49.8999.... Cent-precision matters for AT.
  • Safe retries — GET retries with exponential backoff + jitter. POST retries only when external_reference is set (Vendus's deduplication anchor). Without it, POST fails immediately to avoid duplicate fiscal documents.
  • PII redaction — fiscal_id, email, phone, address are automatically redacted from logs.
  • NIF validation — Portuguese NIF check digit verified locally before any API call.
  • Exception hierarchy — catch ValidationError for local issues, AuthenticationError for bad keys, RateLimitError for 429s, or VendusError for everything.
  • AT communication is opaque — Vendus is the certified party. Hash, ATCUD, and QR code come ready from Vendus; the SDK never talks to AT directly.

Development

git clone https://github.com/bilouro/vendus-python.git
cd vendus-python
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install

Run checks (all must pass before a PR):

ruff check .          # lint
ruff format --check . # formatting
mypy src/             # type check (strict)
pytest                # unit tests + coverage (≥85% enforced)

Live integration tests hit the real Vendus API; they're excluded from pytest and auto-skip without credentials. Run them in test mode against a demo account:

export VENDUS_API_KEY=... VENDUS_REGISTER_ID=...
pytest -m integration --no-cov

Full developer guide — testing, the live-validation discipline, and how to add a document type — is on the Contributing page.

Contributing

See CONTRIBUTING.md. PRs welcome — especially for new document types.

Security

Report vulnerabilities privately — see SECURITY.md. Do not open public issues for security bugs.

License

MIT — use it however you want.

Metadata

Release files for vendus 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vendus 0.1.2
File Size Uploaded Provenance
vendus-0.1.2.tar.gz 73.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for vendus 0.1.2
File Interpreter ABI Platform
vendus-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 102.2 kB

Release files / vendus-0.1.2.tar.gz

Download URL vendus-0.1.2.tar.gz
Size 73.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a14496ecf0f25a467bc058e3655ba5c81d886c650cf9ec52e1f19dd617ba820f
BLAKE2b-256 checksum
How to use checksums
dfbb4da0f4ede952cb8d9c08533f28ec0560895800d3b5a71561d67e92292154
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.

Transparency log

Release files / vendus-0.1.2-py3-none-any.whl

Download URL vendus-0.1.2-py3-none-any.whl
Size 28.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e44c638b3650c13ea5d8da3ea98e0b4031e018b5d32d2381317c496ab0b5880
BLAKE2b-256 checksum
How to use checksums
e1de396ee788d08961bd7355ffac7f1341827a9c4f93d6619af18cafb0486935
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.

Transparency log

Security for vendus 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vendus-0.1.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page