Published: April 2026
He was afraid of nothing—except maybe forgetting to save the backup.
def upload_receipt(pdf_path, description="Business lunch"): files = "file": (os.path.basename(pdf_path), open(pdf_path, "rb"), "application/pdf")
| Rule | Detail | |------|--------| | | UTF‑8 without BOM. Windows‑1252 will cause “invalid character” errors. | | Delimiter | Comma ( , ) – you can also use semicolon ( ; ) if you tick “Semicolon‑separated” in the import dialog. | | Header Row | Mandatory – column names must match Lexoffice’s field names (see the tables below). | | Date Format | ISO 8601 ( YYYY‑MM‑DD ). Lexoffice will auto‑detect DD.MM.YYYY but it’s slower. | | Decimal Separator | Dot ( . ). If you use a comma, enable the “European number format” toggle. | | Quote Handling | Enclose fields that contain commas, line‑breaks, or quotes in double quotes ( " ). |
He opened the lexoffice import center. A clean, white dashboard. Two buttons: and Import .
Below are the minimal columns required for each import type. Extra columns are ignored, so feel free to add notes or custom fields.
Over the next hour, Felix became a mad scientist. He scanned train tickets. He imported a CSV from his online shop (which previously required a blood sacrifice to format). He even uploaded a blurry photo of a restaurant bill taken at midnight—the AI read it perfectly, assigning the "Business Meals (70% deductible)" category.
API_URL = "https://api.lexoffice.io/v2/contacts" TOKEN = os.getenv("LEXOFFICE_TOKEN") # Set in environment
Once you have fileId , you can associate it with an expense line item in an invoice or a generic “expense” entry.
He clicked , zipped the file, and emailed Mrs. Gänsewein with a subject line he'd never dared write before: "All ready. Early. I know."