API Integration Guide — Automate Invoicing, CRM & Accounting
docs101 exposes its full functionality as a REST API, enabling developers to automate invoice creation, customer management, and accounting exports from any language or platform. Every invoice generated through the API is automatically EU-compliant — ZUGFeRD and XRechnung XML is embedded into the PDF without you having to handle XML schemas or PDF/A conversion. Whether you're building a SaaS billing pipeline, syncing your CRM, or feeding DATEV exports to your accountant, the API has you covered. This guide covers what the API can do. For a hands-on walkthrough, see the step-by-step tutorial.
API access requires an active Pro subscription. See Quickstart to generate your first API key.
What You Can Build
The docs101 API is designed for developers who need to integrate invoicing into their existing systems. Here are some common use cases:
- SaaS billing automation — Trigger invoice creation from your subscription system when a billing cycle completes
- ERP / CRM sync — Create customers and invoices from your CRM, keep both systems in sync
- Agency invoicing — Import time-tracking data (e.g., from Clockify) and auto-generate client invoices
- Accounting pipeline — Schedule nightly DATEV exports to feed your accountant's workflow
- White-label invoicing — Use docs101 as the invoicing engine behind your own product
How It Works
All API requests are authenticated via an X-API-Key header and return JSON responses. PDF generation and accounting exports run asynchronously — you enqueue a job and poll for its status until it completes. The base URL for all endpoints is https://docs101.com/api/v1.
Invoicing — Create EU-Compliant Invoices
The invoice lifecycle follows a clear path: create a draft, add line items (positions), finalize the invoice via an asynchronous job, and download the resulting PDF. The generated PDF is ZUGFeRD/XRechnung-compliant with embedded XML — no extra steps needed. You can also send invoices by email directly through the API.
| Action | Method | Endpoint | Notes |
|---|---|---|---|
| List invoices | GET | /invoice/ | Paginated, filterable |
| Create invoice | POST | /invoice/ | Returns { "invoice_id": ... } |
| Get invoice | GET | /invoice/{id} | Full invoice object |
| Update invoice | PUT | /invoice/{id} | Draft status only |
| Add position | POST | /invoice/{id}/positions | Line items with quantity, price, tax |
| Reorder positions | PUT | /invoice/{id}/positions/reorder | Array of position IDs |
| Validate | GET | /invoice/{id}/validate | Pre-flight check with VAT validation |
| Finalize (async) | POST | /invoice/{id}/job | Enqueues PDF generation job |
| Poll job status | GET | /invoice/{id}/job/{job_id} | Returns queued / started / finished / failed |
| Download PDF | GET | /invoice/{id}/pdf-link | Presigned S3 URL |
| Download XML | GET | /invoice/{id}/xml-link | ZUGFeRD/XRechnung XML |
| Send email | POST | /invoice/{id}/send_email | With optional BCC and attachments |
| Mark as sent | PUT | /invoice/{id}/status | { "status": "SENT" } |
| Mark as paid | PUT | /invoice/{id}/status | { "status": "PAID", "paid_date": "...", "payment_method": "..." } |
| Cancel invoice | POST | /invoice/{id}/cancel | Irreversible |
| Create credit note | POST | /invoice/{id}/credit-note | From existing invoice |
| Manage attachments | GET / POST | /invoice/{id}/attachments | PDF, DOCX, XLSX, images (5 MB limit) |
| Preview PDF | GET | /invoice/{id}/preview | Draft preview without finalizing |
# Create an invoice
invoice = requests.post(f"{BASE_URL}/invoice/", headers=HEADERS, json={
"customer_id": 42,
"ftl_template_id": 1,
"invoice_date": "2025-03-15",
"tax_treatment_id": "standard",
}).json()
# Add a line item
requests.post(f"{BASE_URL}/invoice/{invoice['invoice_id']}/positions", headers=HEADERS, json={
"description": "Consulting — March 2025",
"quantity": 10,
"unit_price": 150.00,
"tax_rate": 0.19,
"unit_id": 5,
})
Finalization runs asynchronously. After POST /invoice/{id}/job, poll the returned job_id until the status is finished. The step-by-step tutorial covers the full polling pattern.
CRM — Manage Customers, Addresses & VAT
docs101 handles both B2B and B2C customers. B2B customers can have their EU VAT ID validated automatically against the VIES database. Each customer can have multiple addresses (billing, shipping), and you can generate customer account statements as PDFs.
| Action | Method | Endpoint | Notes |
|---|---|---|---|
| List customers | GET | /customer/ | Paginated, search, email lookup |
| Create customer | POST | /customer/ | Returns { "id": ... } |
| Get customer | GET | /customer/{id} | Full customer object |
| Update customer | PUT | /customer/{id} | |
| Manage addresses | GET / POST | /customer/{id}/addresses | Billing, shipping |
| Validate VAT ID | GET | /customer/{id}/vat-check | Async VIES validation |
| Financial overview | GET | /customer/{id}/financial-overview | Outstanding / paid totals |
| Account statement | GET | /customer/{id}/statement | JSON data with period filter |
| Statement PDF | GET | /customer/{id}/statement/pdf | Presigned download URL |
# Create a B2B customer
customer = requests.post(f"{BASE_URL}/customer/", headers=HEADERS, json={
"organization_name": "Acme GmbH",
"email": "billing@acme.de",
"vat_id": "DE123456789",
"contact_type": "B2B",
}).json()
# Add a billing address
requests.post(
f"{BASE_URL}/customer/{customer['id']}/addresses",
headers=HEADERS,
json={
"address_line_1": "Musterstraße 1",
"postal_code": "10115",
"city": "Berlin",
"country_code": "DE",
"type": "billing",
},
)
When you create a B2B customer with a vat_id, docs101 automatically validates it against the EU VIES database. If the ID cannot be confirmed, the invoice finalization step will ask you to explicitly confirm with vat_override_confirmed: true.
Accounting — DATEV Export & Download Management
docs101 can export invoices in DATEV format for German accountants. The export is configurable — you define GL account mappings for your revenue accounts — and runs asynchronously. Exports that span multiple calendar years are automatically split into separate files. All export files are available as ZIP downloads via presigned URLs.
| Action | Method | Endpoint | Notes |
|---|---|---|---|
| Trigger DATEV export | POST | /exports/job | { "task": "process_datev_export_job", ... } |
| Poll export status | GET | /exports/job/{job_id} | Status + result object on completion |
| Get DATEV config | GET | /datev/config | Company-wide export settings |
| Update DATEV config | PUT | /datev/config | |
| Get account mappings | GET | /datev/mappings | GL account assignments |
| Update mappings | PUT | /datev/mappings | Batch save |
| Reset mappings | POST | /datev/mappings/reset | Restore defaults |
| List downloads | GET | /downloads/ | All available export files |
| Download file | GET | /downloads/{id}/download | Presigned S3 URL |
| Delete download | DELETE | /downloads/{id} | Remove export record |
# Trigger a DATEV export
job = requests.post(f"{BASE_URL}/exports/job", headers=HEADERS, json={
"task": "process_datev_export_job",
"start_date": "2025-01-01",
"end_date": "2025-03-31",
}).json()
# Poll until finished
import time
while True:
status = requests.get(
f"{BASE_URL}/exports/job/{job['job_id']}", headers=HEADERS
).json()
if status["status"] == "finished":
break
time.sleep(2)
# Download all export files
for download_id in status["result"]["download_ids"]:
dl = requests.get(
f"{BASE_URL}/downloads/{download_id}/download", headers=HEADERS
).json()
print(f"Download: {dl['url']}")
If your export period spans multiple calendar years, docs101 automatically splits it into separate files per year. The result object includes is_split: true and a split_reason field explaining why.
Manual vs. API — Side by Side
| Task | In the Web App | Via API |
|---|---|---|
| Create a customer | Fill out form, click Save | POST /customer/ with JSON body |
| Create an invoice | Select customer, fill positions, click Create | POST /invoice/ → POST .../positions |
| Generate PDF | Click Finalize | POST .../job → poll → GET .../pdf-link |
| Send by email | Click Send, enter recipient | POST .../send_email with recipient in body |
| Export to DATEV | Exports page → click Export | POST /exports/job → poll → GET .../download |
| Check VAT ID | Automatic on customer save | GET /customer/{id}/vat-check |
Everything you can do in the web app, you can do via the API — and automate it.
Beyond Invoicing
The API also covers additional areas of docs101:
- Templates — List, configure, and preview invoice templates (
/template/) - Products — Manage your product catalog for quick position entry (
/products/) - Company settings — Update company details, logo, bank accounts, payment terms (
/company/) - Invoice numbering — Configure numbering sequences with date placeholders (
/numbering/) - Team management — Invite members, manage roles (
/members/) - Audit log — Track all changes for compliance (
/audit-log/)
Get Started
- Quickstart — Generate your first API key and make a test request in 5 minutes
- Step-by-Step Tutorial — Full walkthrough from customer creation to DATEV export
- Python SDK — Official Python client library for the docs101 API
- Swagger UI — Interactive API reference with all endpoints and schemas