Outgoing Invoices
This section explains how to create, send, retrieve, and manage outgoing invoices via faktoora's e-Invoicing API. Invoices are created asynchronously, with webhook notifications or polling available to track status and fetch the resulting documents.
Creation Modes
When creating invoices via POST /invoices, you can choose one of three modes in the request payload:
- Draft (
draftOnly=true):
The invoice is created in draft status and requires manual confirmation in the UI before sending. - Review (
reviewMode=true):
The invoice enters review status; a user must approve in the UI before sending. - Direct (no draft or review flags):
The invoice is created and sent automatically upon generation.
Outgoing Invoice API Endpoints
All invoice endpoints require
apiKeyAuth.
| Endpoint | Method | Description |
|---|---|---|
POST /invoices | POST | Create one or more invoices (202 if all succeed, an error status mirroring the first failure — usually 400, or 500 — if any invoice fails). Returns faktooraId and detailsPage; on failure, created invoices are listed in extra.results. |
GET /invoices | GET | List invoices with pagination and filtering (keyword, invoiceNumber, date ranges, sort). |
GET /invoices?invoiceNumber= | GET | Download a single invoice by invoiceNumber (PDF or XML). Set Accept header accordingly. |
GET /invoices/{faktooraId} | GET | Download an invoice by faktooraId (PDF or XML). Set Accept header accordingly. |
GET /invoices/{faktooraId}/status | GET | Poll invoice processing status (authority registration, delivery, validation). Use after POST 202. |
GET /invoices/{faktooraId}/validation-report | GET | Download the PDF validation report for the most recent validation. Returns 404 if none exists. |
DELETE /invoices/{faktooraId} | DELETE | Delete an invoice. Requires invoiceDeleteMode to be enabled on the account. |
Example: Create invoice
A complete, ready-to-send payload. draftOnly: true keeps the invoice editable in the UI and sends
nothing, so it is safe to replay while integrating. format, issueDate, invoiceNumber,
invoiceTypeCode, invoiceItems and buyer are required; seller is required in practice, because
XRechnung and Peppol need at least one of seller.peppolId, seller.email or contactPerson.email.
curl -X POST \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"invoices": [
{
"format": "zf:2",
"schema": "CII",
"invoiceTypeCode": 380,
"invoiceNumber": "INV-1001",
"issueDate": "20260630",
"dueDate": "20260714",
"currency": "EUR",
"draftOnly": true,
"seller": {
"name": "Musterfirma GmbH",
"street": "Hauptstraße 15",
"postcode": "80331",
"city": "München",
"country": "DE",
"vatId": "DE123456789",
"email": "kontakt@musterfirma.de",
"banking": {
"iban": "DE89370400440532013000",
"owner": "Musterfirma GmbH"
}
},
"buyer": {
"name": "Beispielkunde AG",
"street": "Friedrichstraße 50",
"postcode": "10117",
"city": "Berlin",
"country": "DE",
"email": "einkauf@beispielkunde.de",
"vatId": "DE987654321"
},
"specifiedTradeSettlementPaymentMeans": [
{ "typeCode": "58", "information": "Zahlung per Überweisung" }
],
"invoiceItems": [
{
"id": "1",
"product": {
"name": "Consulting",
"quantity": 8,
"unitCode": "HUR",
"price": 150,
"taxes": [
{ "typeCode": "VAT", "categoryCode": "S", "rate": 19 }
]
}
}
]
}
]
}' \
https://api.faktoora.com/api/v1/invoices
Response (202 Accepted — all invoices succeeded)
[
{
"faktooraId": "INV123456",
"detailsPage": "https://app.faktoora.com/invoice/details/..."
}
]
Response (400 Bad Request — at least one invoice failed)
The standard error object is returned — code, message and any structured error details in extra reflect the first failed invoice. In addition, extra.results contains a list aligned to the input order (element [i] corresponds to invoices[i]): successfully created invoices are not dropped — their faktooraId/detailsPage appear at their input positions, alongside the error for the failed ones.
The status mirrors the first failed invoice's error: usually 400 for a validation error (other 4xx statuses, e.g. 404 for a missing reference, are possible), or 500 if it hit an unexpected server error (the element then carries code: "INTERNAL_SERVER_ERROR"). All carry the same extra.results list — reconcile any failure status exactly the same way.
{
"code": "E_VALIDATION",
"statusCode": 400,
"message": "invoiceItems[0].product.taxes[0]: taxCategoryCode \"Z\" requires rate = 0 (EN 16931 BR-Z-05)",
"extra": {
"results": [
{
"success": true,
"faktooraId": "INV123456",
"detailsPage": "https://app.faktoora.com/invoice/details/..."
},
{
"success": false,
"error": {
"code": "E_VALIDATION",
"message": "invoiceItems[0].product.taxes[0]: taxCategoryCode \"Z\" requires rate = 0 (EN 16931 BR-Z-05)"
}
}
]
}
}
A 400 (or 500) response does not mean nothing was created — elements with success: true in extra.results have already been persisted. Reconcile using extra.results and re-submit only the failed elements. Re-POSTing the full batch can create duplicate invoices: duplicate invoiceNumber detection only rejects the retry when the invoice number is explicitly set on a non-draft invoice and your account does not allow duplicate invoice numbers. Invoices with auto-generated numbers (no invoiceNumber in the payload) or draftOnly: true will simply be created again.
Example: Poll invoice status
curl -H "X-API-KEY: your-api-token" \
https://api.faktoora.com/api/v1/invoices/INV123456/status
Response (200 OK)
{
"invoiceStatus": "created",
"jobStatus": "done",
"authority": [],
"delivery": [
{
"type": "email",
"recipient": "buyer@example.com",
"carbonCopy": null,
"blindCarbonCopy": null
}
],
"validation": {
"status": "success",
"validity": true,
"createdAt": "2026-03-26T12:34:56Z"
}
}
jobStatus summarizes generation: processing (files still being generated, or the
invoice is awaiting authority acceptance), done (all files for the invoice's format are
generated and any authority step is complete — ready to download), or failed (the most
recent attempt failed; it clears on a successful retry, so the field can move back to
processing/done). It is omitted for drafts. Poll until jobStatus is "done" before
downloading the invoice.
invoiceStatus progresses through: draft → review → created → sent.
The validation field is present only if validation was triggered (via validate: true on creation or manually in the UI). See Invoice Validation for details.
Example: Retrieve created invoice
# Download by faktooraId as PDF
curl -H "X-API-KEY: your-api-token" \
-H "Accept: application/pdf" \
https://api.faktoora.com/api/v1/invoices/INV123456
# Download by invoiceNumber as XML
curl -G -H "X-API-KEY: your-api-token" \
-H "Accept: application/xml" \
--data-urlencode "invoiceNumber=INV-1001" \
https://api.faktoora.com/api/v1/invoices
Example: List invoices with filtering
curl -G -H "X-API-KEY: your-api-token" \
--data-urlencode "keyword=Acme" \
--data-urlencode "page=1" \
--data-urlencode "perPage=20" \
--data-urlencode "sort=issueDate" \
--data-urlencode "order=desc" \
https://api.faktoora.com/api/v1/invoices
Response (200 OK) — array of invoice items with pagination metadata.
Example: Delete an invoice
curl -X DELETE \
-H "X-API-KEY: your-api-token" \
https://api.faktoora.com/api/v1/invoices/INV123456
Deletion requires the invoiceDeleteMode feature to be enabled for your account. Returns 204 No Content on success.
Invoice Validation
You can automatically validate generated invoices against XRechnung/ZUGFeRD standards by setting validate: true in your POST /invoices request. Validation runs asynchronously after invoice file generation and produces a downloadable PDF report.
Requires the
invoiceValidationfeature to be enabled on your account. If the feature is not enabled, thevalidateparameter is silently ignored.
Triggering Validation
Add validate: true to any invoice in the creation payload. This example is an XRechnung for a
public-sector buyer, so it also carries the Leitweg-ID in buyerReference.buyerReferenceId:
curl -X POST \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"invoices": [
{
"format": "xrechnung",
"schema": "CII",
"invoiceTypeCode": 380,
"invoiceNumber": "INV-1001",
"issueDate": "20260330",
"currency": "EUR",
"validate": true,
"draftOnly": true,
"seller": {
"name": "Musterfirma GmbH",
"street": "Hauptstraße 15",
"postcode": "80331",
"city": "München",
"country": "DE",
"vatId": "DE123456789",
"email": "kontakt@musterfirma.de",
"banking": {
"iban": "DE89370400440532013000",
"owner": "Musterfirma GmbH"
}
},
"buyer": {
"name": "Bundesamt für Beispiele",
"street": "Friedrichstraße 50",
"postcode": "10117",
"city": "Berlin",
"country": "DE",
"email": "rechnung@beispielamt.de"
},
"buyerReference": { "buyerReferenceId": "991-33333-84" },
"invoiceItems": [
{
"id": "1",
"product": {
"name": "Consulting",
"quantity": 8,
"unitCode": "HUR",
"price": 150,
"taxes": [
{ "typeCode": "VAT", "categoryCode": "S", "rate": 19 }
]
}
}
]
}
]
}' \
https://api.faktoora.com/api/v1/invoices
Checking Validation Status
Poll GET /invoices/{faktooraId}/status — when validation has been triggered, the response includes a validation object:
{
"invoiceStatus": "created",
"jobStatus": "done",
"authority": [],
"delivery": [],
"validation": {
"status": "success",
"validity": true,
"createdAt": "2026-03-26T12:34:56Z"
}
}
| Field | Type | Description |
|---|---|---|
status | string | processing, success, or error |
validity | boolean | null | Whether the invoice passed validation. null while processing. |
createdAt | string | ISO 8601 timestamp of when validation was triggered. |
The validation field is only present if a validation job exists for the invoice. If validate was not set or the feature is not enabled, this field is omitted.
Downloading the Validation Report
Once validation.status is success or error, download the report PDF:
curl -H "X-API-KEY: your-api-token" \
-o validation-report.pdf \
https://api.faktoora.com/api/v1/invoices/INV123456/validation-report
Returns application/pdf. Returns 404 if no validation has been performed or the report is not yet available.
Outgoing Invoice Content Endpoints
Once an outgoing invoice exists (created via POST /invoices or imported via POST /import/outgoing), you can retrieve its structured content via the /invoices/{faktooraId} track. These endpoints provide machine-readable access to the invoice's parsed data.
All endpoints below require
apiKeyAuth.
| Endpoint | Method | Description |
|---|---|---|
GET /import/outgoing | GET | Paginated list of all outgoing imported invoices. |
POST /import/outgoing | POST | Import an existing XRechnung or ZUGFeRD file (multipart/form-data, field: file). Returns { faktooraId }. |
GET /import/outgoing/{faktooraId}/status | GET | Import processing status with validationStatus, visualizationStatus, and overallStatus (accepted, failed, or pending). Distinct from /invoices/{faktooraId}/status (invoice lifecycle). |
GET /invoices/{faktooraId}/content/summary | GET | Key invoice attributes summary. |
GET /invoices/{faktooraId}/content | GET | Merged complete content (seller, buyer, line items, taxes, payment terms, payment means). |
GET /invoices/{faktooraId}/content/lineitems | GET | Array of invoice line items. |
GET /invoices/{faktooraId}/content/taxes | GET | VAT breakdown by category. |
GET /invoices/{faktooraId}/content/paymentterms | GET | Payment terms (e.g., due date, Skonto conditions). |
GET /invoices/{faktooraId}/content/paymentmeans | GET | Payment methods (bank transfer, SEPA, etc.). |
GET /invoices/{faktooraId}/content/seller | GET | Seller snapshot from the invoice. |
GET /invoices/{faktooraId}/content/buyer | GET | Buyer snapshot from the invoice. |
GET /invoices/{faktooraId}/content/delivery | GET | Delivery address from the invoice. |
GET /invoices/{faktooraId}/content/pdf | GET | PDF visual representation of the invoice. |
GET /invoices/{faktooraId}/source | GET | Original source file (XML preferred over PDF). Returns application/xml or application/pdf. |
GET /invoices/{faktooraId}/validation | GET | JSON validation summary. Returns { "status": "accepted" } or { "status": "rejected" }. |
GET /invoices/{faktooraId}/validation/pdf | GET | PDF validation report. |
GET /invoices/{faktooraId}/attachments | GET | Paginated list of embedded attachments extracted from the invoice. |
GET /invoices/{faktooraId}/attachment/{attachmentId} | GET | Metadata for a single attachment. |
GET /invoices/{faktooraId}/attachment/{attachmentId}/file | GET | Download the attachment file (application/octet-stream). |
The per-document /invoices/{faktooraId}/... paths above are also reachable under the legacy /import/outgoing/{faktooraId}/... prefix; both invoke the same handler. New integrations should use the /invoices/{faktooraId}/... form.
Example: Fetch invoice content summary
curl -H "X-API-KEY: your-api-token" \
https://api.faktoora.com/api/v1/invoices/INV123456/content/summary
Example: Check import status
curl -H "X-API-KEY: your-api-token" \
https://api.faktoora.com/api/v1/import/outgoing/INV123456/status
Response (200 OK)
{
"validationStatus": "accepted",
"visualizationStatus": "accepted",
"overallStatus": "accepted"
}
Webhook Notifications
Upon invoice creation or status changes, faktoora sends a webhook to your configured endpoint:
- status=draft, status=review, or status=created
- Payload includes
faktooraId,status,timestamp, and invoice metadata.
Process Diagrams
Sequence Diagram
Illustrates the full lifecycle from client submission through sending and retrieval.
Flowchart
Shows conditional flows for creation modes, webhooks, and retrieval.