Skip to main content

Invoice API Usage Guide

API Version: v1.0 Microservice: XDPeople.Soba.Invoice.WebAPI


Overview

The Invoice API manages the lifecycle of sales documents through a single create endpoint. One call — POST /gateway/invoice/invoices — creates documents whose behaviour is determined by the documentTypeId.

Supported document natures

NatureCodeDedicated guide
Sales invoiceFTInvoices Guide
Simplified invoiceFSInvoices Guide
Invoice-receiptFRInvoice-Receipt Guide
Credit noteNCCredit Notes Guide
Debit noteNDDebit Notes Guide
OrderNEOrders Guide
QuoteORCQuotes Guide
Transport guidesGT, GR, GA, GC, GDTransport Guides

Allowed payment mechanisms

Any payments[] entry must use a payment type whose mechanism (paymentMechanism) is in the list below. This restriction applies to all documents, regardless of whether payments are required or optional. Requests send paymentTypeId.

CodeDescription
NUCash
CCCredit card
CDDebit card
OUOther

Resolve valid identifiers with GET /gateway/payment-types and pick records whose paymentMechanism matches one of the codes above.

Validation

The API validates the mechanism of each payments[] entry when creating a document. Payment types with other mechanisms (for example TB, CH, MB, MW) are rejected, even if they are active in the client's database.

Checking-account payment type rules

Checking-account payments not supported

This API does not accept payment types with SendToCheckingAccount = true. Any attempt to use them returns Payment.CheckingAccount.NotSupported. Document types configured with CheckingAccountFlow != 0 or MoveSetting = CheckingAccount require exactly that kind of payment type — making them incompatible with this API.

When the document type has checking-account requirements, the payment type supplied in payments[] must satisfy them. If it does not, the payment entry is rejected:

Document type conditionError
MoveSetting = CheckingAccount and payment type does not post to checking accountPayment.CheckingAccountType.RequiredForMoveSetting
Payment mode has IncrementDays > 0 and payment type is not checking-accountPayment.CheckingAccountType.RequiredForDeferredMode
CheckingAccountFlow != 0 and payment type is not checking-accountPayment.CheckingAccountType.RequiredForDocumentFlow
Receipts (RE) not available via the Invoice API

Receipt nature RE cannot be created through this API. Use invoice-receipt (FR) when payment is collected on the same document, or consult product documentation for checking-account flows. The Receipts Guide is kept for reference only.

Other natures (proforma, CAS, DIS, etc.) are not available via this API and return DocumentType.NotSupported.

Key features

  • End-to-end document creation (validation, totals, payments, persistence)
  • Document creation, update, and query (flat and full formats)
  • Advanced filters and pagination
  • JWT authentication
  • Timezone-aware date persistence via X-Timezone header

How document creation works

When you call POST /gateway/invoice/invoices, the API:

  1. Initial validation — supported nature, country/licence, payment mechanisms, series, customer, issuance rules
  2. Header preparation — entity resolution, transport defaults, fiscal checks
  3. Calculations — line and header totals, taxes, discounts
  4. Payments — split payments, change; for document types with optional payments, at most one entry with no split or change
  5. Finalization — ATCUD, digital signature, legal observations
  6. Transactional persistence — header, lines, references, stock, counters
Stock movement

Stock movement follows the document type and item configuration.


Technical information

Base URL

EnvironmentBase URLDocumentation
Productionhttps://api.xdsoba.com/gatewaySwagger UI

Request headers

HeaderRequiredDescription
AuthorizationYesBearer {jwt}
Content-TypeYes (POST/PUT)application/json
X-TimezoneRecommendedIANA timezone (e.g. Europe/Lisbon). Dates are stored in the local timezone.

Health checks

GET /gateway/invoice/health

Authentication

POST https://api.xdsoba.com/gateway/auth/combined-login
Content-Type: application/json

{
"tenantKey": "XDPT.30010",
"tenantPassword": "your-tenant-password",
"userKey": "user@example.com",
"userPassword": "your-user-password"
}

Use the token in all requests:

Authorization: Bearer {token}
X-Timezone: Europe/Lisbon

Resolving document type IDs

The numeric documentTypeId is not portable between client databases. Resolve it with:

GET /gateway/document-type
Authorization: Bearer {token}

Use each row's id as documentTypeId. Match description / keyId to the business process (quote, order, invoice, credit note, etc.).

tip

Call GET /gateway/document-type before integration tests. Replace illustrative IDs in the guides with values returned by GET /gateway/document-type.

See Creating document types (API Base) if a nature is missing from your document types.

Creating document types (API Base)

POST /gateway/document-type
Content-Type: application/json
Authorization: Bearer {token}
{
"keyId": "ORC",
"description": "Quote",
"documentType": 0,
"invoiceType": "ORC",
"entityType": 0,
"checkingAccountFlow": 0,
"stockFlow": 0,
"stockBehaviour": 0,
"cashierFlow": 0,
"defaultEntityType": 0,
"printSystemType": 0,
"instances": 1,
"isValued": true,
"voucherExpirationDays": 0,
"voucherDeductibleAfter": 0,
"isAdvancement": false,
"forceAskBatches": false,
"defaultAccountId": 0,
"reportLayout": "",
"htmlLayout": ""
}

Available endpoints

Create document

POST /gateway/invoice/invoices
Content-Type: application/json
Authorization: Bearer {token}
X-Timezone: Europe/Lisbon

Creates a sales document (nature determined by documentTypeId).

Update document

PUT /gateway/invoice/invoices/{id}

Get document by ID

GET /gateway/invoice/invoices/{id}
GET /gateway/invoice/invoices/{id}/flat
  • /flat — backward-compatible flat format for integrations
  • Without /flat — full format for printing (header + bodies)

Get document by type, series, and number

GET /gateway/invoice/invoices/{documentTypeId}/{serieId}/{number}
GET /gateway/invoice/invoices/{documentTypeId}/{serieId}/{number}/flat

List documents

GET /gateway/invoice/invoices?page=1&pageSize=20&status=Open&startDate=2025-01-01&endDate=2025-12-31
Authorization: Bearer {token}
ParameterDescription
pagePage number (default: 1)
pageSizePage size (default: 20)
statusOpen / Closed
startDateDocuments created on or after this date
endDateDocuments created on or before this date
entityKeyIdFilter by customer/supplier
documentTypeIdFilter by document type
sortByCreationDate, DocumentNumber, Total, EntityDescription
sortDescendingDescending order (default: false)

Data model (create document)

Required fields

{
"serieId": 1,
"documentTypeId": 5,
"currencyId": 1,
"documentBodies": [
{
"itemKeyId": "PROD001",
"quantity": 2,
"retailPrice": 50,
"taxId": 1,
"secondTaxId": 0
}
],
"payments": [
{
"paymentTypeId": 1,
"amount": 123
}
]
}
FieldNotes
currencyIdRequired in the API contract (> 0). Effective currency is currently taken from terminal/configuration.
paymentsRequired when the document type has cashier flow or checking-account flow active. Optional when neither flow applies — omit or supply at most one entry matching the document total. Transport guides (GT…GD), orders (NE), and quotes (ORC) are typically configured without either flow.
discountPercentageOptional header discount (0–100). Line-level: documentBodies[].discountPercentage.

Entity (customer) resolution

entityKeyId is optional. When resolving the entity the API follows this order:

  1. entityKeyId supplied — the entity is loaded directly by key. If not found, the request fails immediately with Entity.KeyId.NotFound (404). No fallback is attempted.
  2. entityKeyId omitted, entityVat supplied — a lookup by VAT is performed. If a match is found, that entity is used. If not, a temporary snapshot is created using the supplied override fields (entityDescription, entityAddress, etc.) with the final-consumer key.
  3. Both omitted — the configured default final consumer is loaded from the database.
caution

Providing an entityKeyId that does not exist is a hard error (404). The entityVat field is ignored in this case — correct or omit entityKeyId first.

Common optional fields

{
"entityDescription": "Example Client, Ltd",
"entityVat": "PT123456789",
"entityAddress": "123 Example Street",
"entityPostalCode": "1000-000",
"entityCity": "Lisbon",
"entityCountry": "Portugal",
"documentDate": "2025-11-28T10:00:00Z",
"dueDate": "2025-12-31",
"paymentModeId": 1,
"obs": "Document notes",
"docReference": "2;1;2026-06-05;FAC;;FAC;",
"extraDocReference": "EXTRA-REF",
"emissionReason": "Return of goods",
"loadPlaceDescription": "Central Warehouse;Lisbon;1000-100;PT",
"unloadPlaceDescription": "Customer Store;Porto;4000-000;PT",
"loadPlaceDate": "2025-11-28T10:00:00",
"unloadPlaceDate": "2025-11-28T15:00:00",
"carrierDescription": "XYZ Carrier",
"taxRegionId": 1,
"numberPersons": 2
}

Load and unload places fields

FieldDescription
loadPlaceDescriptionOrigin / load place (max 100 chars)
unloadPlaceDescriptionDestination / unload place (max 100 chars)
loadPlaceDateLoad date and time (date-time)
unloadPlaceDateUnload date and time (date-time)
carrierDescriptionCarrier name or description (max 100 chars)

Load address (loadPlaceDescription)

When omitted, the engine resolves a default in this order:

  1. Caller-supplied value — used as-is.
  2. LoadUnloadPlace associated with the terminal's default warehouse (structured format).
  3. Licence address (structured format).
  4. Licence fiscal name / commercial name as fallback.

Unload address (unloadPlaceDescription)

When omitted, the engine resolves in this order:

  1. Caller-supplied value — used as-is.
  2. Customer's EntityAddress marked as DefaultShipmentAddress = true (structured format).
Structured address format

The expected format is "address;city;postalCode;country" — four semicolon-separated segments. When the field is present in the request, it should follow this same format. When omitted and automatically resolved by the engine, multi-part addresses are formatted the same way.

Dates

When loadPlaceDate or unloadPlaceDate are omitted:

DateDefault value
loadPlaceDateCreation time + 15 minutes (or LoadDateAdditionMinutes when configured)
unloadPlaceDateEnd of the next calendar day at 23:59

LoadDateAdditionMinutes is a system configuration value (XConfigMisc). When not configured, 15 minutes is used.

Ignored on FS, NC and ND

loadPlaceDescription, unloadPlaceDescription, and related load and unload place fields are ignored for simplified invoices (FS), credit notes (NC), and debit notes (ND). Do not rely on them on those natures.

Line fields (reference documents — NC/ND)

On each referenced line, only originBodyGuid, relationType, and quantity are required. The API derives itemKeyId, taxId, retailPrice, and netPrice from the origin line when omitted:

{
"quantity": 1,
"originBodyGuid": "660e8400-e29b-41d4-a716-446655440001",
"relationType": 1
}
  • Obtain originBodyGuid from GET /gateway/invoice/invoices/{documentTypeId}/{serieId}/{number}/flat (field guid on each line).
  • relationType: 1 = ReferenceOrigin (required for NC/ND).
  • Quantities are positive; the document type determines credit/debit semantics.
  • To override prices (e.g. value correction), supply retailPrice / netPrice explicitly. On Angolan documents, prices must match totalAmount / quantity from the origin — see the dedicated guides for details.

For the full field reference, derived-field rules, and Angola-specific restrictions, see the Credit Notes Guide and Debit Notes Guide.

Parent-child lines (guid / parentGuid)

Menus and composite articles are different catalogue concepts (see below). When you send child lines, both use the same linking fields in the payload:

FieldWhereDescription
guidParent line onlyClient-generated UUID that identifies the parent line in the request
parentGuidChild linesMust equal the parent line's guid

Shared rules:

  • One level only — child lines cannot have their own children.
  • Child lines linked with parentGuid are treated as informative and are excluded from the fiscal total; the parent line carries the sale value.
  • Generate a new UUID for each parent line in every request.

A menu is a catalogue article of type Menu. The customer or operator selects options from the article's menu composition (e.g. starter, main course, side dish). Each selected option is sent as a child line.

The parent itemKeyId must be the menu article. Child itemKeyId values are the chosen options.

Example: Document with menu.

Composite article

A composite article is a catalogue article of type Composite — a kit or finished product with components defined in composite detail in the catalogue.

Unlike a menu, you do not need to send the component articles in documentBodies. A single line with the composite itemKeyId is enough — the same shape as a normal article line (no guid / parentGuid required).

Optionally, you may add child lines with parentGuid if you want the components listed explicitly on the document (quantities and prices follow the composite article setup).

Example: Document with composite article.


Usage examples

Simple invoice with payment

POST /gateway/invoice/invoices
Content-Type: application/json
Authorization: Bearer {token}
X-Timezone: Europe/Lisbon
{
"serieId": 1,
"documentTypeId": 5,
"entityKeyId": "CLI001",
"currencyId": 1,
"entityDescription": "Example Client, Ltd",
"entityVat": "PT123456789",
"paymentModeId": 1,
"payments": [
{
"paymentTypeId": 1,
"amount": 123
}
],
"documentBodies": [
{
"itemKeyId": "PROD001",
"itemDescription": "Product 1",
"quantity": 2,
"retailPrice": 50,
"taxId": 1,
"secondTaxId": 0
}
]
}

Response (201 Created) includes documentNumber, total, totalTaxes, change (when applicable), and guid.

Split payment

{
"payments": [
{
"paymentTypeId": 1,
"amount": 50
},
{
"paymentTypeId": 3,
"amount": 73
}
]
}

The sum of amount values must match the delivered total. Change is calculated automatically when the payment type allows it.

Use when selling a menu article: parent line with guid (the menu) and child lines with parentGuid (selected options). Item keys, prices, and payment total are illustrative — resolve real values via the API.

POST /gateway/invoice/invoices
Content-Type: application/json
Authorization: Bearer {token}
X-Timezone: Europe/Lisbon
{
"serieId": 1,
"documentTypeId": 1,
"entityKeyId": "1",
"entityVat": "555666777",
"currencyId": 1,
"obs": "Document notes.",
"saleZoneAreaObjectId": 1,
"payments": [
{
"paymentTypeId": 1,
"amount": 1140
}
],
"documentBodies": [
{
"guid": "06a646ac-1101-4252-8fd9-20c7173ded78",
"itemKeyId": "353",
"quantity": 1,
"retailPrice": 10,
"taxId": 1
},
{
"parentGuid": "06a646ac-1101-4252-8fd9-20c7173ded78",
"itemKeyId": "366",
"quantity": 1,
"retailPrice": 0,
"taxId": 1
},
{
"parentGuid": "06a646ac-1101-4252-8fd9-20c7173ded78",
"itemKeyId": "354",
"quantity": 1,
"retailPrice": 0,
"taxId": 1
}
]
}

Item 353 is the menu article; 366 and 354 are the selected options. The sum of payments[].amount must match the total calculated by the API.

Document with composite article

Use when selling a composite article. Send only the composite line — component articles from composite detail do not need to be included in the request.

POST /gateway/invoice/invoices
Content-Type: application/json
Authorization: Bearer {token}
X-Timezone: Europe/Lisbon
{
"serieId": 1,
"documentTypeId": 5,
"entityKeyId": "CLI001",
"currencyId": 1,
"payments": [
{
"paymentTypeId": 1,
"amount": 49.9
}
],
"documentBodies": [
{
"itemKeyId": "KIT001",
"quantity": 1,
"retailPrice": 49.9,
"taxId": 1
}
]
}

Item KIT001 must be a catalogue article of type Composite. The API resolves components from the article's composite detail — you do not send COMP-A, COMP-B, or similar unless you explicitly want them as informative child lines on the printed document.

List open documents

GET /gateway/invoice/invoices?status=Open&page=1&pageSize=20
Authorization: Bearer {token}

Error handling

CodeWhen
201 CreatedDocument created
200 OKUpdate or query success
400 Bad RequestValidation failed
401 UnauthorizedMissing/invalid token
404 Not FoundDocument not found
409 ConflictConcurrency conflict
500 Internal Server ErrorUnexpected error

Common validation codes: DocumentType.NotSupported, Payment.InsufficientValue, SaleDocumentRules.NCND.*, DocumentReferenceBalance.*.


Best practices

  • Always send X-Timezone for correct local dates
  • Resolve documentTypeId with GET /gateway/document-type — never hard-code guide examples
  • Use payments[] at header level for documents that require payment
  • For NC/ND: set emissionReason; use per-line GUID refs for in-app origins (PT/AO)
  • Menus: send selected options as child lines with guid / parentGuid — see menu example
  • Composite articles: a single line with the composite itemKeyId is enough — see composite example

Specialized guides

GuideTopic
QuotesORC
OrdersNE
InvoicesFT, FS
Invoice-ReceiptFR
Credit NotesNC
Debit NotesND
Transport GuidesGT, GR, GA, GC, GD
ReceiptsRE (reference — not available via API)

Last Updated: July 7, 2026