> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e-invoice.be/llms.txt
> Use this file to discover all available pages before exploring further.

# Document

> Look up each field of the Document schema and see how the API converts it to UBL BIS Billing 3.0.

## Overview

The `Document` schema defines the structure of invoices, credit notes and debit notes in the e-invoice.be API. You send this schema when you call `POST /api/documents/` or `POST /api/validate/json`.

<Note>
  Only the `items` array is required, with a minimum of one line. All other fields are optional, but a document needs most of them to pass the Peppol BIS Billing 3.0 rules.
</Note>

<Note>
  Validation is not a separate mandatory call. `POST /api/documents/` rejects a payload that does not pass the same rules. Use `POST /api/validate/json` while you develop, because it returns all rule failures and the generated UBL.
</Note>

## Document metadata

<ParamField body="document_type" type="enum" default="INVOICE">
  Type of document to create.

  * `INVOICE` - Standard invoice
  * `CREDIT_NOTE` - Credit note (refund or correction)
  * `DEBIT_NOTE` - Debit note
  * `SELFBILLING_INVOICE` - Self-billed invoice
  * `SELFBILLING_CREDIT_NOTE` - Self-billed credit note

  See [Self-billing and debit notes](/guides/self-billing) for the two self-billing types and for debit notes.
</ParamField>

<ParamField body="state" type="enum" default="DRAFT">
  Document state. See [Document states](#document-states).
</ParamField>

<ParamField body="direction" type="enum" default="OUTBOUND">
  Document direction.

  * `OUTBOUND` - Document that you send to a customer
  * `INBOUND` - Document that you receive from a supplier
</ParamField>

### Document states

| State | Meaning |
| - | - |
| `DRAFT` | The document exists but is not sent. `POST /api/documents/` always creates a document in this state. |
| `TRANSIT` | The send request is accepted and the transmission is in progress. |
| `SENT` | The transmission is complete. |
| `FAILED` | The transmission failed. You can send the document again. |
| `RECEIVED` | The document came in from another Peppol participant. |

You can send a document only when it is in the `DRAFT` or `FAILED` state. For the transitions, the retry behaviour and the webhook events of each state, see [Document lifecycle and delivery tracking](/guides/document-lifecycle).

See [Document lifecycle and delivery tracking](/guides/document-lifecycle) for the state diagrams, the transitions, the retries and the related webhook events.

## Vendor (supplier) information

<ParamField body="vendor_name" type="string">
  Legal name of the vendor.

  Example: `"E-INVOICE BV"`
</ParamField>

<ParamField body="vendor_tax_id" type="string">
  VAT number of the vendor, with the country prefix. The API changes the value to uppercase and removes all characters other than A-Z and 0-9. See [Normalisation and rounding](#normalisation-and-rounding).

  Example: `"BE1018265814"`

  The API derives the Peppol participant ID of the sender from this value when it sends the document. See [Look up Peppol participants](/guides/lookup-participants) for Peppol routing.
</ParamField>

<ParamField body="vendor_address" type="string">
  Address of the vendor as one string. See [Addresses](#addresses) for the format that the API can split correctly.

  Example: `"Brusselsesteenweg 119/A, 1980 Zemst, BE"`
</ParamField>

<ParamField body="vendor_address_recipient" type="string">
  Department or person at the vendor address. The API writes this value as the contact name only when `vendor_email` is present.

  Example: `"Accounts Department"`
</ParamField>

<ParamField body="vendor_email" type="string">
  Contact email address of the vendor.

  Example: `"billing@e-invoice.be"`
</ParamField>

## Customer (buyer) information

<ParamField body="customer_name" type="string">
  Legal name of the customer.

  Example: `"OpenPeppol VZW"`
</ParamField>

<ParamField body="customer_tax_id" type="string">
  VAT number of the customer, with the country prefix. The API changes the value to uppercase and removes all characters other than A-Z and 0-9.

  Example: `"BE0848934496"`
</ParamField>

<ParamField body="customer_peppol_id" type="string">
  Peppol participant ID of the customer, in the format `scheme:identifier`. When you send this field, the API uses it as the receiver of the document and does not derive the receiver from `customer_tax_id`.

  Example: `"0208:0848934496"`

  The API does not store this field with the document. The value is kept only as the receiver of the generated UBL. See [Look up Peppol participants](/guides/lookup-participants) to find and check a Peppol ID before you send.
</ParamField>

<ParamField body="customer_id" type="string">
  Your internal reference for the customer. The API writes it as the party identification of the customer. It is also the fallback value of `BuyerReference` when `purchase_order` is absent.

  Example: `"CUST-12345"`
</ParamField>

<ParamField body="customer_address" type="string">
  Address of the customer as one string. See [Addresses](#addresses).

  Example: `"Rond-point Schuman 6, 1040 Brussels, BE"`
</ParamField>

<ParamField body="customer_address_recipient" type="string">
  Department or person at the customer address. The API writes this value as the contact name only when `customer_email` is present.

  Example: `"Accounts Payable"`
</ParamField>

<ParamField body="customer_email" type="string">
  Contact email address of the customer.

  Example: `"ap@openpeppol.example"`
</ParamField>

## Invoice details

<ParamField body="invoice_id" type="string">
  Unique invoice number.

  Example: `"INV-2026-001"`
</ParamField>

<ParamField body="invoice_date" type="string">
  Issue date in ISO 8601 format (`YYYY-MM-DD`).

  Example: `"2026-10-01"`
</ParamField>

<ParamField body="due_date" type="string">
  Payment due date in ISO 8601 format (`YYYY-MM-DD`). The API does not write this field to the UBL of a credit note.

  Example: `"2026-10-31"`
</ParamField>

<ParamField body="purchase_order" type="string">
  Purchase order reference of the customer. For credit notes, use this to reference the original invoice.

  Example: `"PO-12345"` or `"INV-2026-001"` (for credit notes)
</ParamField>

<ParamField body="note" type="string">
  Free-text note.

  Example: `"Full refund - goods returned"`
</ParamField>

<ParamField body="payment_term" type="string">
  Description of the payment terms.

  Example: `"Payment due within 30 days"`
</ParamField>

## Financial fields

The API calculates `subtotal`, `total_discount`, `total_tax`, `invoice_total` and `amount_due` when they are absent. When you send them, the API compares them with its own calculation. See [Invoice totals and calculations](/guides/invoice-totals) for the formulas.

<ParamField body="currency" type="enum" default="EUR">
  Currency code (ISO 4217).

  The `currency` field accepts these ISO 4217 codes:

  `EUR`, `USD`, `GBP`, `JPY`, `CHF`, `CAD`, `AUD`, `NZD`, `CNY`, `INR`, `SEK`, `NOK`, `DKK`, `SGD`, `HKD`

  The default is `EUR`.

  Example: `"EUR"`
</ParamField>

<ParamField body="subtotal" type="number">
  Taxable base amount (after document-level allowances and charges, before tax).

  Corresponds to UBL `cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount`

  Example: `1000.00`
</ParamField>

<ParamField body="total_discount" type="number">
  Total of the document-level allowances (discounts only, not charges).

  Corresponds to UBL `cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount`

  Example: `50.00`
</ParamField>

<ParamField body="total_tax" type="number">
  Total VAT amount.

  Corresponds to UBL `cac:TaxTotal/cbc:TaxAmount`

  Example: `210.00`
</ParamField>

<ParamField body="invoice_total" type="number">
  Total amount with tax (`subtotal` + `total_tax`).

  Corresponds to UBL `cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount`

  Example: `1210.00`
</ParamField>

<ParamField body="amount_due" type="number">
  Amount due for payment after prepayments.

  Corresponds to UBL `cac:LegalMonetaryTotal/cbc:PayableAmount`

  Example: `1210.00`
</ParamField>

<ParamField body="previous_unpaid_balance" type="number">
  Previous outstanding balance. This is an internal field: the API does not write it to the generated UBL.

  Example: `100.00`
</ParamField>

## Tax information

<ParamField body="tax_code" type="enum" default="S">
  Tax category code (UNCL5305).

  * `S` - Standard rate (most common)
  * `Z` - Zero rated
  * `E` - Exempt from tax
  * `AE` - VAT reverse charge
  * `K`, `G`, `O`, `L`, `M`, `B` - Other special cases

  When `tax_code` is `K` (intra-community supply), each line item must have a `tax_rate` of `0.00`.
</ParamField>

<ParamField body="vatex" type="enum">
  VAT exemption reason code (when `tax_code` is `E`, `AE`, `K`, `G`, `O`, `L`, `M` or `B`).

  Example: `"VATEX-EU-IC"` for an intra-community supply
</ParamField>

<ParamField body="vatex_note" type="string">
  Text that explains the VAT exemption.

  Example: `"Reverse charge applies - Art. 196 EU VAT Directive"`
</ParamField>

## Service period

<ParamField body="service_start_date" type="string">
  Start date of the service period (ISO 8601: `YYYY-MM-DD`).

  Example: `"2026-09-01"`
</ParamField>

<ParamField body="service_end_date" type="string">
  End date of the service period (ISO 8601: `YYYY-MM-DD`).

  Example: `"2026-09-30"`
</ParamField>

## Additional addresses

<Note>
  Of these fields, the API writes only `shipping_address` and `shipping_address_recipient` to the generated UBL (as `cac:Delivery`). The billing, service and remittance fields are kept with the document, but the receiver does not get them in the UBL.
</Note>

<ParamField body="billing_address" type="string">
  Billing address (if different from the customer address).

  Example: `"Rond-point Schuman 6, 1040 Brussels, BE"`
</ParamField>

<ParamField body="billing_address_recipient" type="string">
  Recipient at the billing address.

  Example: `"Accounts Payable Department"`
</ParamField>

<ParamField body="shipping_address" type="string">
  Delivery address. The API writes it to `cac:Delivery/cac:DeliveryLocation/cac:Address` with the same split rule as the party addresses.

  Example: `"Industrieweg 5, 3000 Leuven, BE"`
</ParamField>

<ParamField body="shipping_address_recipient" type="string">
  Recipient at the delivery address. The API writes it as the name of the delivery party.

  Example: `"Warehouse Manager"`
</ParamField>

<ParamField body="service_address" type="string">
  Address of the service location.

  Example: `"Industrieweg 5, 3000 Leuven, BE"`
</ParamField>

<ParamField body="service_address_recipient" type="string">
  Recipient at the service address.
</ParamField>

<ParamField body="remittance_address" type="string">
  Remittance (payment) address.

  Example: `"Brusselsesteenweg 119/A, 1980 Zemst, BE"`
</ParamField>

<ParamField body="remittance_address_recipient" type="string">
  Recipient at the remittance address.
</ParamField>

## Line items

<ParamField body="items" type="array" required>
  Array of line items (minimum 1).

  See the [LineItem schema](/api-reference/schemas/line-item) for the fields.

  Example:

  ```json theme={null}
  [
    {
      "description": "Professional services",
      "quantity": 10,
      "unit": "C62",
      "unit_price": 100.00,
      "amount": 1000.00,
      "tax_rate": 21.00
    }
  ]
  ```
</ParamField>

## Payment details

<ParamField body="payment_details" type="array">
  Array of payment instructions. The API writes one `cac:PaymentMeans` element for each entry, always with payment means code `30` (credit transfer).

  <Expandable title="properties">
    <ParamField body="iban" type="string">
      International Bank Account Number. The API changes the value to uppercase and removes all characters other than A-Z and 0-9.

      Example: `"BE68539007547034"`
    </ParamField>

    <ParamField body="swift" type="string">
      SWIFT/BIC code of the bank. The API writes it only when `iban` or `bank_account_number` is present.

      Example: `"GEBABEBB"`
    </ParamField>

    <ParamField body="bank_account_number" type="string">
      Bank account number for an account that has no IBAN. The API writes it as the payee account only when `iban` is absent. The API does not normalise this value.

      Example: `"12345678"`
    </ParamField>

    <ParamField body="payment_reference" type="string">
      Payment reference, for example a Belgian structured communication. When it is absent, the API writes `invoice_id` as the payment reference.

      Example: `"+++010/8068/17183+++"`
    </ParamField>
  </Expandable>

  Example:

  ```json theme={null}
  [
    {
      "iban": "BE68539007547034",
      "swift": "GEBABEBB",
      "payment_reference": "INV-2026-001"
    }
  ]
  ```
</ParamField>

## Allowances and charges

<ParamField body="allowances" type="array">
  Document-level allowances (discounts).

  <Expandable title="properties">
    <ParamField body="amount" type="number">
      Allowance amount without VAT, with a maximum of 2 decimals. The API does not calculate this value from `multiplier_factor` and `base_amount`. You must send it.

      Example: `100.00`
    </ParamField>

    <ParamField body="reason" type="string">
      Reason for the allowance as text.

      Example: `"Volume discount"`
    </ParamField>

    <ParamField body="reason_code" type="enum">
      Allowance reason code (UNCL5189). Permitted values: `41`, `42`, `60`, `62`, `63`, `64`, `65`, `66`, `67`, `68`, `70`, `71`, `88`, `95`, `100`, `102`, `103`, `104`, `105`. Send the code as a string.

      Example: `"95"` (discount)
    </ParamField>

    <ParamField body="multiplier_factor" type="number">
      Percentage of the allowance, from 0 to 100, with a maximum of 2 decimals. To state 10%, send `10`. Send it together with `base_amount`.

      Example: `10`
    </ParamField>

    <ParamField body="base_amount" type="number">
      Amount to which the percentage applies, with a maximum of 2 decimals. Send it together with `multiplier_factor`.

      Example: `1000.00`
    </ParamField>

    <ParamField body="tax_code" type="enum" default="S">
      VAT category code that applies to the allowance.
    </ParamField>

    <ParamField body="tax_rate" type="number" default="21.00">
      VAT rate that applies to the allowance, as a percentage from 0 to 100.
    </ParamField>
  </Expandable>

  Example of a 10% allowance:

  ```json theme={null}
  [
    {
      "reason": "Volume discount",
      "reason_code": "95",
      "multiplier_factor": 10,
      "base_amount": 1000.00,
      "amount": 100.00,
      "tax_code": "S",
      "tax_rate": 21.00
    }
  ]
  ```
</ParamField>

<ParamField body="charges" type="array">
  Document-level charges (fees).

  <Expandable title="properties">
    <ParamField body="amount" type="number">
      Charge amount without VAT, with a maximum of 2 decimals. The API does not calculate this value from `multiplier_factor` and `base_amount`. You must send it.

      Example: `25.00`
    </ParamField>

    <ParamField body="reason" type="string">
      Reason for the charge as text.

      Example: `"Shipping and handling"`
    </ParamField>

    <ParamField body="reason_code" type="enum">
      Charge reason code (UNCL7161). The full list of permitted values is in the `ChargeReasonCode` schema of the [API reference](/api-reference).

      Example: `"ABL"` (additional packaging)
    </ParamField>

    <ParamField body="multiplier_factor" type="number">
      Percentage of the charge, from 0 to 100, with a maximum of 2 decimals. To state 2.5%, send `2.5`. Send it together with `base_amount`.

      Example: `2.5`
    </ParamField>

    <ParamField body="base_amount" type="number">
      Amount to which the percentage applies, with a maximum of 2 decimals. Send it together with `multiplier_factor`.

      Example: `1000.00`
    </ParamField>

    <ParamField body="tax_code" type="enum" default="S">
      VAT category code that applies to the charge.
    </ParamField>

    <ParamField body="tax_rate" type="number" default="21.00">
      VAT rate that applies to the charge, as a percentage from 0 to 100.
    </ParamField>
  </Expandable>

  Example:

  ```json theme={null}
  [
    {
      "reason": "Additional packaging",
      "reason_code": "ABL",
      "amount": 25.00,
      "tax_code": "S",
      "tax_rate": 21.00
    }
  ]
  ```
</ParamField>

See [Advanced invoicing](/guides/advanced-invoicing) for the effect of allowances and charges on the totals.

## Tax details

<ParamField body="tax_details" type="array">
  Tax breakdown for each category and rate.

  The API calculates the breakdown when it is absent.
</ParamField>

## Attachments

<ParamField body="attachments" type="array">
  Files in this array are embedded in the UBL. See [Attachments and PDF](/guides/attachments) for the permitted file types and for the `construct_pdf` option.

  Example:

  ```json theme={null}
  [
    {
      "file_name": "timesheet.pdf",
      "file_type": "application/pdf",
      "file_data": "<base64-encoded file data>"
    }
  ]
  ```
</ParamField>

## How fields map to UBL

The API converts the JSON document to UBL BIS Billing 3.0. The table shows the conversions that are not self-evident. The response of `POST /api/validate/json` contains the generated UBL, thus you can examine the result for your own payload.

| JSON field | UBL element | Rule |
| - | - | - |
| `purchase_order` | `cbc:BuyerReference` and `cac:OrderReference/cbc:ID` | When `purchase_order` is absent, `BuyerReference` is `customer_id`. When `customer_id` is also absent, it is the text `Unknown`. |
| `payment_details[].payment_reference` | `cac:PaymentMeans/cbc:PaymentID` | When the reference is absent, the value is `invoice_id`. |
| `payment_details[]` | `cac:PaymentMeans/cbc:PaymentMeansCode` | Always `30` (credit transfer). |
| `payment_details[].iban`, `bank_account_number` | `cac:PayeeFinancialAccount/cbc:ID` | `iban` when it is present, else `bank_account_number`. |
| `payment_details[].swift` | `cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID` | Only when an account is present. |
| `payment_term` | `cac:PaymentTerms/cbc:Note` | |
| `due_date` | `cbc:DueDate` | Invoices only. Not written for credit notes. |
| `service_start_date`, `service_end_date` | `cac:InvoicePeriod/cbc:StartDate`, `cbc:EndDate` | Written when one or the two dates are present. |
| `vendor_email`, `customer_email` | `cac:Party/cac:Contact/cbc:ElectronicMail` | |
| `vendor_address_recipient`, `customer_address_recipient` | `cac:Party/cac:Contact/cbc:Name` | Only when the email of the same party is present. |
| `customer_id` | `cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID` | |
| `shipping_address` | `cac:Delivery/cac:DeliveryLocation/cac:Address` | |
| `shipping_address_recipient` | `cac:Delivery/cac:DeliveryParty/cac:PartyName/cbc:Name` | |
| `allowances[]`, `charges[]` | `cac:AllowanceCharge` | `reason_code` goes to `cbc:AllowanceChargeReasonCode`, `multiplier_factor` to `cbc:MultiplierFactorNumeric`, `base_amount` to `cbc:BaseAmount`. |
| `items[].product_code` | `cac:Item/cac:SellersItemIdentification/cbc:ID` | |
| `items[].item_attributes[]` | `cac:Item/cac:AdditionalItemProperty` | An attribute without `value` is not written. |
| `items[].unit_price` | `cac:Price/cbc:PriceAmount` | `cac:Price/cbc:BaseQuantity` is always `1`. |
| `attachments[]` | `cac:AdditionalDocumentReference` | See the MIME type rule below. |

The MIME type of an attachment in the UBL is `file_type` when it is one of `application/pdf`, `image/png`, `image/jpeg`, `text/csv`, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` (xlsx) or `application/vnd.oasis.opendocument.spreadsheet` (ods). For a different `file_type`, the API uses the extension of `file_name` (`.pdf`, `.png`, `.jpg`, `.jpeg`, `.csv`, `.xlsx`, `.ods`) to find the MIME type.

## Addresses

`vendor_address`, `customer_address` and `shipping_address` are single strings. The API splits each string into the parts of a UBL address with these rules:

1. The API splits the string on new lines. If there is only one line, it splits the string on a comma followed by a space (`", "`).
2. Part 1 is the street (`cbc:StreetName`).
3. Part 2 is the postal code and the city. When the part has the form "postal code, space, city", the API writes `cbc:PostalZone` and `cbc:CityName`. If not, the full part is the city.
4. The last part is the country (`cac:Country/cbc:IdentificationCode`) only if it has 2 or 3 letters and no other characters. The API changes it to uppercase.
5. For the vendor and the customer, if the last part is not a country code, the API uses the first two characters of the tax ID of that party. If there is no tax ID, the country is `BE`. For `shipping_address`, the API does not use the tax ID: the country is `BE` when the string has no country code.

Use this format, with the ISO 3166-1 alpha-2 country code as the last part:

```json theme={null}
{
  "vendor_address": "Brusselsesteenweg 119/A, 1980 Zemst, BE",
  "customer_address": "Rond-point Schuman 6, 1040 Brussels, BE"
}
```

The first address gives this UBL:

```xml theme={null}
<cac:PostalAddress>
  <cbc:StreetName>Brusselsesteenweg 119/A</cbc:StreetName>
  <cbc:CityName>Zemst</cbc:CityName>
  <cbc:PostalZone>1980</cbc:PostalZone>
  <cac:Country>
    <cbc:IdentificationCode>BE</cbc:IdentificationCode>
  </cac:Country>
</cac:PostalAddress>
```

<Warning>
  The API does not read a country name as a country. With `"..., 1000 Brussels, Belgium"`, the API ignores `Belgium` and takes the country from the tax ID prefix, or uses `BE`. An address in a different country, with a country name and without a tax ID, gets the country `BE`. Always end the address with the two-letter country code.
</Warning>

<Warning>
  Do not put a comma followed by a space in the street part (for example `"Main Street 1, box 5"`). The API reads the text after the comma as the postal code and city. A last part that has only 2 or 3 letters is always read as a country code.
</Warning>

When the address is absent or has no usable parts, the API writes `Unknown Street` and `Unknown City`.

## Normalisation and rounding

The API changes some values before it stores the document and generates the UBL.

| Field | Rule |
| - | - |
| `vendor_tax_id`, `customer_tax_id` | Changed to uppercase. All characters other than A-Z and 0-9 are removed. `"be 0848.934.496"` becomes `"BE0848934496"`. |
| `payment_details[].iban` | Same rule. `"BE68 5390 0754 7034"` becomes `"BE68539007547034"`. |
| `amount`, `tax` and `tax_rate` of a line item | 2 decimals. |
| `quantity` and `unit_price` of a line item | 4 decimals. |
| `amount`, `base_amount`, `multiplier_factor` and `tax_rate` of an allowance or charge | 2 decimals. |

Rounding of monetary amounts is half-up to 2 decimals (`0.005` becomes `0.01`). The API rounds in this sequence:

1. Each line `amount` is rounded.
2. For each tax group, the API adds the rounded line amounts, subtracts the document-level allowances and adds the document-level charges. A tax group is one combination of tax category and tax rate.
3. The tax of each group is `taxable amount × rate / 100`, rounded.
4. `total_tax` is the sum of the group taxes.

The API thus calculates VAT for each tax group, not for each line. If you calculate VAT for each line and add the results, your `total_tax` can be different by some cents. See [Invoice totals and calculations](/guides/invoice-totals).

## Example

Complete invoice:

```json Invoice theme={null}
{
  "document_type": "INVOICE",
  "invoice_id": "INV-2026-001",
  "invoice_date": "2026-10-01",
  "due_date": "2026-10-31",
  "currency": "EUR",
  "purchase_order": "PO-12345",
  "vendor_name": "E-INVOICE BV",
  "vendor_tax_id": "BE1018265814",
  "vendor_address": "Brusselsesteenweg 119/A, 1980 Zemst, BE",
  "vendor_email": "billing@e-invoice.be",
  "customer_name": "OpenPeppol VZW",
  "customer_tax_id": "BE0848934496",
  "customer_address": "Robert Schumanplein 6 bus 5, 1040 Brussel, BE",
  "items": [
    {
      "description": "Professional services",
      "quantity": 10,
      "unit": "C62",
      "unit_price": 100.00,
      "amount": 1000.00,
      "tax_rate": "21.00"
    }
  ],
  "subtotal": 1000.00,
  "total_tax": 210.00,
  "invoice_total": 1210.00,
  "amount_due": 1210.00,
  "payment_term": "Payment within 30 days",
  "payment_details": [
    {
      "iban": "BE68539007547034",
      "swift": "GEBABEBB",
      "payment_reference": "INV-2026-001"
    }
  ]
}
```

Replace the vendor fields with the data of your company. The API rejects a document if the Peppol ID of the vendor is not one of the `peppol_ids` of your company.

## Next Steps

<CardGroup cols={2}>
  <Card title="LineItem" icon="list-ol" href="/api-reference/schemas/line-item">
    Look up the fields of an invoice line.
  </Card>

  <Card title="Create e-invoices" icon="file-invoice" href="/guides/creating-invoices">
    Create and send an invoice with this schema.
  </Card>

  <Card title="Invoice totals and calculations" icon="calculator" href="/guides/invoice-totals">
    Calculate the totals that the API checks.
  </Card>

  <Card title="Validation during development" icon="circle-check" href="/guides/validation">
    Validate a payload and examine the generated UBL.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.