Skip to main content

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.

EndpointMethodDescription
POST /invoicesPOSTCreate 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 /invoicesGETList invoices with pagination and filtering (keyword, invoiceNumber, date ranges, sort).
GET /invoices?invoiceNumber=GETDownload a single invoice by invoiceNumber (PDF or XML). Set Accept header accordingly.
GET /invoices/{faktooraId}GETDownload an invoice by faktooraId (PDF or XML). Set Accept header accordingly.
GET /invoices/{faktooraId}/statusGETPoll invoice processing status (authority registration, delivery, validation). Use after POST 202.
GET /invoices/{faktooraId}/validation-reportGETDownload the PDF validation report for the most recent validation. Returns 404 if none exists.
DELETE /invoices/{faktooraId}DELETEDelete 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)"
}
}
]
}
}
Do not blind-retry a failed batch

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 invoiceValidation feature to be enabled on your account. If the feature is not enabled, the validate parameter 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"
}
}
FieldTypeDescription
statusstringprocessing, success, or error
validityboolean | nullWhether the invoice passed validation. null while processing.
createdAtstringISO 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.

EndpointMethodDescription
GET /import/outgoingGETPaginated list of all outgoing imported invoices.
POST /import/outgoingPOSTImport an existing XRechnung or ZUGFeRD file (multipart/form-data, field: file). Returns { faktooraId }.
GET /import/outgoing/{faktooraId}/statusGETImport processing status with validationStatus, visualizationStatus, and overallStatus (accepted, failed, or pending). Distinct from /invoices/{faktooraId}/status (invoice lifecycle).
GET /invoices/{faktooraId}/content/summaryGETKey invoice attributes summary.
GET /invoices/{faktooraId}/contentGETMerged complete content (seller, buyer, line items, taxes, payment terms, payment means).
GET /invoices/{faktooraId}/content/lineitemsGETArray of invoice line items.
GET /invoices/{faktooraId}/content/taxesGETVAT breakdown by category.
GET /invoices/{faktooraId}/content/paymenttermsGETPayment terms (e.g., due date, Skonto conditions).
GET /invoices/{faktooraId}/content/paymentmeansGETPayment methods (bank transfer, SEPA, etc.).
GET /invoices/{faktooraId}/content/sellerGETSeller snapshot from the invoice.
GET /invoices/{faktooraId}/content/buyerGETBuyer snapshot from the invoice.
GET /invoices/{faktooraId}/content/deliveryGETDelivery address from the invoice.
GET /invoices/{faktooraId}/content/pdfGETPDF visual representation of the invoice.
GET /invoices/{faktooraId}/sourceGETOriginal source file (XML preferred over PDF). Returns application/xml or application/pdf.
GET /invoices/{faktooraId}/validationGETJSON validation summary. Returns { "status": "accepted" } or { "status": "rejected" }.
GET /invoices/{faktooraId}/validation/pdfGETPDF validation report.
GET /invoices/{faktooraId}/attachmentsGETPaginated list of embedded attachments extracted from the invoice.
GET /invoices/{faktooraId}/attachment/{attachmentId}GETMetadata for a single attachment.
GET /invoices/{faktooraId}/attachment/{attachmentId}/fileGETDownload 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.