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: draftreviewcreatedsent.

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.