Sending Invoices
This guide shows how to send invoices to KSEF using the library.
Prerequisites
Before sending invoices, you must be authenticated with KSEF.
from ksef.auth.token import TokenAuthorization
from ksef.client import Client
from ksef.constants import Environment
auth = TokenAuthorization(token="your-token", environment=Environment.TEST)
auth.authorize(nip="1234567890")
client = Client(authorization=auth, environment=Environment.TEST)
Creating an Invoice
First, create an Invoice object with all required data:
from datetime import date, datetime, timezone
from decimal import Decimal
from ksef.models.invoice import (
Address,
Invoice,
InvoiceData,
InvoiceType,
Issuer,
IssuerIdentificationData,
Subject,
SubjectIdentificationData,
)
from ksef.models.invoice_annotations import (
FreeFromVat,
IntraCommunitySupplyOfNewTransportMethods,
InvoiceAnnotations,
MarginProcedure,
ReverseCharge,
SelfInvoicing,
SimplifiedProcedureBySecondTaxPayer,
SplitPayment,
TaxSettlementOnPayment,
)
from ksef.models.invoice_rows import InvoiceRow, InvoiceRows
invoice = Invoice(
# Issuer (seller) information
issuer=Issuer(
identification_data=IssuerIdentificationData(
nip="1234567890",
full_name="My Company Sp. z o.o.",
),
address=Address(
country_code="PL",
city="Warszawa",
street="Przykładowa",
house_number="1",
apartment_number="2A",
postal_code="00-001",
),
email="contact@example.com",
phone="+48 123456789",
),
# Recipient (buyer) information
recipient=Subject(
identification_data=SubjectIdentificationData(
nip="0987654321",
),
),
# Invoice details
invoice_data=InvoiceData(
currency_code="PLN",
issue_date=date.today(),
issue_number="FV/2026/001",
sell_date=date.today(),
total_amount=Decimal("1230.00"),
invoice_type=InvoiceType.REGULAR_VAT,
# Invoice line items
invoice_rows=InvoiceRows(rows=[
InvoiceRow(name="Consulting services", tax=23),
InvoiceRow(name="Software license", tax=23),
]),
# Required annotations (usually all NO for standard invoices)
invoice_annotations=InvoiceAnnotations(
tax_settlement_on_payment=TaxSettlementOnPayment.REGULAR,
self_invoice=SelfInvoicing.NO,
reverse_charge=ReverseCharge.NO,
split_payment=SplitPayment.NO,
free_from_vat=FreeFromVat.NO,
intra_community_supply_of_new_transport_methods=IntraCommunitySupplyOfNewTransportMethods.NO,
simplified_procedure_by_second_tax_payer=SimplifiedProcedureBySecondTaxPayer.NO,
margin_procedure=MarginProcedure.NO,
),
),
# Creation timestamp (required for FA(3) schema)
creation_datetime=datetime.now(tz=timezone.utc),
)
Sending with the Convenience Method
The simplest way to send an invoice is using the send_invoice() method, which handles the entire session lifecycle automatically:
response = client.send_invoice(nip="1234567890", invoice=invoice)
print(f"Invoice submitted! Reference: {response.reference_number}")
This method:
- Opens an encrypted session with KSEF
- Encrypts and sends your invoice
- Closes the session
- Returns the submission reference number
Asynchronous Processing
KSEF processes invoices asynchronously. The reference_number returned is for tracking - the actual KSEF invoice number (ksef_reference_number) is assigned after processing completes.
Sending Multiple Invoices (Session-Based)
For sending multiple invoices efficiently, use the session-based approach:
# Open a session
session_context = client.open_session(nip="1234567890")
try:
# Send multiple invoices in the same session
for invoice in invoices:
response = client.send_invoice_in_session(
session_context=session_context,
invoice=invoice,
)
print(f"Submitted: {response.reference_number}")
finally:
# Always close the session
client.close_session(session_context)
Session Benefits
Using a single session for multiple invoices is more efficient because:
- Encryption keys are generated only once
- Fewer API round-trips
- Better performance for batch operations
Invoice Types
The library supports all KSEF invoice types:
| Type | Enum Value | Description |
|---|---|---|
| Regular VAT | InvoiceType.REGULAR_VAT |
Standard VAT invoice |
| Correction | InvoiceType.CORRECTION |
Correction invoice |
| Advance | InvoiceType.ADVANCE |
Advance payment invoice |
| Settlement | InvoiceType.SETTLEMENT |
Settlement invoice |
| Simplified | InvoiceType.SIMPLIFIED |
Simplified invoice |
| Correction Advance | InvoiceType.CORRECTION_ADVANCE |
Correction of advance invoice |
| Correction Settlement | InvoiceType.CORRECTION_SETTLEMENT |
Correction of settlement invoice |
Error Handling
Handle potential errors during invoice submission:
from requests.exceptions import HTTPError
try:
response = client.send_invoice(nip="1234567890", invoice=invoice)
print(f"Success: {response.reference_number}")
except HTTPError as e:
print(f"KSEF API error: {e.response.status_code}")
print(f"Details: {e.response.text}")
except Exception as e:
print(f"Error: {e}")
Next Steps
- Download invoices to retrieve invoice XML
- Learn about invoice annotations for special cases