Invoices
Create and send invoices to the UAE Peppol network. The invoice is built from structured data, validated, converted to XML and dispatched. Validate a payload first with the dry-run endpoint to catch rule failures before sending. Selling to a customer outside the UAE? Start with Export invoice details below — an export is in scope, but it identifies its buyer differently and has its own required fields. Requires an access token.
You send structured business data. We build the UAE PINT-AE UBL document from it, validate it against the PINT-AE rules, and deliver it to the Peppol network — adding the specification identifiers, the seller party and a number of derived values along the way.
POST /oauth/invoices/validate— optional dry run. Same rules, nothing stored.POST /oauth/invoices— the invoice is stored and queued. You get 202 and aninvoice_id.- We validate, convert to PINT XML and deliver to Peppol, asynchronously.
- You follow progress through the status endpoint or a webhook. A 202 is not a successful send.
The minimum a call must carry. Everything else is either optional or supplied by us — see Default PINT XML values.
entity_id | The seller entity to invoice from. It must belong to your application — list them with GET /oauth/entities. |
invoice_number | Your own invoice number. It must be unique for the entity; a repeat is rejected with 409. |
date | Issue date, YYYY-MM-DD. |
buyer.name | The buyer's legal name. |
buyer.country | Buyer country — a name or an ISO 3166-1 alpha-2 code. |
buyer.trn or buyer.participant_id | How the buyer is addressed on the network. A UAE buyer sends its 15-digit TRN; a buyer already on Peppol sends its participant id, with the scheme only when a global registry issued it (GLN, DUNS, LEI) — otherwise the scheme follows from the country. A foreign buyer that sends only its own tax number is addressed from that. An export buyer with neither may omit both — see Export invoice. |
line_items[] | At least one line, each with description, quantity, unit_amount and tax_amount. |
payment_account_id | Your IBAN. Required while payment_means_code is "30" (credit transfer), which is the default — send a different code if you are not being paid by transfer. |
A seller registration type | Either on the entity (legal_registration_type) or per-invoice (seller.registration_type). An AE seller without one is rejected by the gateway under ibr-173-ae, so it is checked before sending. |
Grouped by where they sit in the payload. Anything you omit either falls back to the default in section 5 or is left out of the XML entirely.
Document
due_date | Payment due date, YYYY-MM-DD. |
reference | Free-text note carried on the invoice. |
currency | ISO 4217 code. Defaults to "AED". |
exchange_rate | UAE Central Bank rate to AED. Optional: omit it for a non-AED invoice and the rate published for the invoice date is used (refreshed again when the invoice is sent). A rate you send is used as sent. Ignored for an AED invoice. |
transaction_type_code | The eight BTUAE-02 flags. "00000001" declares an export. |
sub_total / total_tax / total | Computed from your line items when omitted. Send them only if you need to state them exactly. |
Buyer
buyer.address_line1 / city / region | Buyer postal address (IBG-08). Recommended for a UAE buyer. |
buyer.email / phone | Contact details. |
buyer.legal_registration_id | The buyer's trade licence or equivalent (IBT-047). |
buyer.legal_registration_type | Its type — "TL", "EID", "PAS" or "CD". Leave empty for a foreign buyer. |
buyer.authority_name | The issuing authority. Required when the type is "TL". |
buyer.passport_country_code | Required when the type is "PAS". |
Lines
line_items[].tax_category | S, Z, E, O, AE or N. Defaults to "S" — state it for anything else, because a rate alone cannot distinguish them. |
line_items[].tax_rate | VAT percentage. See Default PINT XML values: the emitted rate is derived from the category. |
line_items[].unit | Unit of measure, code or label. Defaults to "EA". |
line_items[].item_name | Product name. Accepted but NOT yet emitted on this API — the item name in the XML always repeats the description, so put the name you want shown there. |
line_items[].line_amount | Net line amount. Computed as quantity × unit_amount when omitted; send it to express a discount. |
line_items[].tax_exemption_reason | Required on an exempt ("E") line. |
line_items[].item_type + item_standard_id | Both required on a reverse-charge ("AE") line. |
line_items[].hs_code / service_code | Classification codes, carried when sent. |
Export only
delivery | Deliver-to address. Required on an export, ignored otherwise. |
customs_declaration_ref | Customs / export declaration number. |
incoterms | Shipment terms, e.g. "CIF". |
deliver_to_party_name / _id / _id_scheme | The delivery recipient on a triangular sale. |
actual_delivery_date | When the goods or services were delivered. |
When this applies
An export is goods or services supplied to a customer outside the UAE — a UAE wholesaler selling to a retailer in Kuwait, a UAE IT firm billing a client in France. Exports are in scope for UAE e-invoicing: they are reported to the FTA like any other invoice, not excluded from it.
Everything on this page applies to POST /oauth/invoices and POST /oauth/invoices/validate. If you send invoices through a connected ERP instead, your PINT mapping already covers exports and nothing here changes for you.
Two things decide whether this applies to you
- 1.Where the customer is. Outside the UAE → export.
- 2.What the customer is. A supply to a private individual is out of scope wherever they are — so an export to a foreign business is in scope, an export to a foreign consumer is not and should not be sent. Gate on buyer type, not on the country alone.
One exclusion to watch: zero-rated financial services supplied to non-residents are outside e-invoicing altogether. Financial services that are standard rated to residents are not excluded, even where they would otherwise qualify as a zero-rated export.
Normal invoice vs export invoice
| Aspect | Normal invoice | Export invoice |
|---|---|---|
Who the buyer is | A UAE business with a 15-digit TRN. | A business outside the UAE. Supplies to a foreign individual consumer are out of scope and must not be sent. |
Document type | Electronic Tax Invoice (380). | Electronic Tax Invoice (380) as well — never a Commercial Invoice. It may also be presented to Customs. |
transaction_type_code | "00000000" (the default). | "00000001" — position 8 is the exports flag. This is what turns the export rules on. |
Buyer address | buyer.trn — its first 10 digits become the Peppol address. | buyer.participant_id when the buyer is on the Peppol network, omitted when it is not. buyer.participant_scheme is optional — it follows from buyer.country — and is sent only for an identifier issued globally rather than nationally, such as a GLN, a DUNS or an LEI. |
Buyer TRN | Required. | Not required and not expected — an overseas buyer has no UAE TRN. Nothing fails for its absence. |
VAT | Standard rated at 5% (tax_category "S"). | Qualifying exports are zero rated: tax_category "Z", tax_rate 0, tax_amount 0.00. Send "S" if the supply genuinely does not qualify. |
delivery block | Optional. | Required — address line 1, city, country subdivision and country. |
Currency | Usually AED. No conversion, and exchange_rate is ignored. | Often USD/EUR. The AED reporting amounts are added for you, at your exchange_rate or at the UAE Central Bank rate for the invoice date. |
Buyer electronic address — what the platform fills in
Every invoice must carry a buyer electronic address, even when the buyer is not reachable on the network. When it cannot be derived from what you sent, the platform substitutes the FTA placeholder for your case — resolved top to bottom, first match wins:
| Condition | Buyer address | When you get it |
|---|---|---|
| Deemed supply | 0235:9900000097 | transaction_type_code position 2 is "1". There is no customer to address, so this wins even when the payload does carry a buyer identifier — and even when the export flag is also set. |
| Export, and the buyer has no Peppol ID | 0235:9900000099 | You declare the export and send neither buyer.trn nor buyer.participant_id. |
| Buyer has no Participant Identifier | 0235:9900000098 | No buyer identifier can be derived at all. A UAE buyer is still required to send its TRN. |
| Buyer has a Participant Identifier | <scheme>:<identifier> | buyer.participant_id is used exactly as sent — it is already a complete address, so it is never truncated and a scheme you state is never re-derived. Omit participant_scheme and it is filled in from buyer.country; state it for a GLN, a DUNS or an LEI, which no country issues. With no participant_id we build one from buyer.trn: for a UAE buyer, 0235 plus the first 10 digits (the UAE TIN); for a foreign buyer, that country’s VAT scheme plus the WHOLE number — the 10-digit rule is UAE-only. Schemes come from the published Peppol EAS list (Italian VAT is 0211, not the withdrawn 9906). A foreign buyer sent under 0235, or a UAE buyer sent under anything else, is rejected. Where a country identifies businesses by a register number with several candidate schemes, or an e-invoicing framework id (DK, SE, NO, FI, SG …), nothing is guessed. That buyer has no usable Peppol ID, so the invoice takes the export placeholder and is filed. Send participant_id when the buyer IS on the network and it is used instead. |
Every case, in full. The four rows above are the FTA's own decision table; this is what it produces for each shape a payload actually arrives in — including the ones where the address is built from the buyer's tax number.
UAE buyer
| What your data holds | Address filed | Result |
|---|---|---|
Tax number (TRN) The first ten digits are the UAE TIN. This is the only truncation in the platform. | 0235:<first 10 digits> | filed |
A participant id, no scheme The scheme follows from the country. An id is already an address, so it is never truncated. | 0235:<id, verbatim> | filed |
No tax number at all The address is filled in and the invoice is still refused: a UAE buyer is required to carry its TRN. | 0235:9900000098 | refused |
Addressed under any scheme but 0235 A UAE buyer is identified in the FTA's own registry. Another scheme means the address was built against the wrong one. | — | refused |
Buyer outside the UAE — with something we can use
| What your data holds | Address filed | Result |
|---|---|---|
Participant id + scheme Used exactly as sent. Never truncated, and a scheme you state is never re-derived. | <scheme>:<id> | filed |
Participant id, no scheme The scheme is filled in from the country. State it yourself for a GLN, DUNS or LEI, which no country issues. | <country scheme>:<id> | filed |
Tax number, country publishes a VAT scheme Germany shown. The WHOLE number is used — the first-10 rule is UAE-only. | 9930:DE123456789 | filed |
Tax number, US / AU / JP / NG No VAT in any of the four, but each publishes exactly one scheme and it is the tax authority's own number: EIN, ABN, qualified-invoice issuer number, Tax ID. | 9959 · 0151 · 0221 · 0244 | filed |
Tax number, Estonia The one deliberate deviation: EE is live on the company code, which is what this platform is registered and sending with. | 0191:<first 8 digits> | filed |
Buyer outside the UAE — with nothing we can use
| What your data holds | Address filed | Result |
|---|---|---|
Tax number, DK SE NO FI SG MY IS Their published identity is a company-register or e-invoicing framework number, not a tax number. Nothing is guessed. | 0235:9900000099 | filed |
Tax number, country publishes nothing (BR) Same answer, for the same reason. | 0235:9900000099 | filed |
No identifier at all The export placeholder, whether or not the transaction type declares the export. | 0235:9900000099 | filed |
Identifier fields sent empty or null Blank is read as absent everywhere. An id is never emitted with an empty value under a scheme. | 0235:9900000099 | filed |
Refused, whatever else is on the invoice
| What your data holds | Address filed | Result |
|---|---|---|
Foreign buyer sent under 0235 0235 is the UAE's own scheme. A foreign buyer addressed with it cannot be delivered to. | — | refused |
A scheme that is not on the Peppol EAS list Checked against the published list, so a typo is caught here rather than by the buyer's access point. | — | refused |
Beats everything above
| What your data holds | Address filed | Result |
|---|---|---|
Deemed supply There is no customer to address, so this wins over a participant id on the payload and over the export flag. | 0235:9900000097 | filed |
A placeholder means no delivery, not no filing. The invoice reaches the authority and is reported like any other; what it does not do is arrive in the buyer's own system, because there is no address to send it to. The three are read differently: 9900000097 a deemed supply, 9900000099 an export whose buyer has no Peppol ID, and 9900000098 a domestic buyer who is not on the network — which is why an export never falls back to the domestic one.
To turn a placeholder into a delivery, supply the buyer's real participant id and scheme — buyer.participant_id and buyer.participant_scheme, or the combined "0088:7300010000001" form. An identifier you supply is always used and is never replaced by a placeholder — including when it is wrong, which is why an unknown scheme is refused rather than quietly swapped.
A placeholder address only means there is no access point to deliver to. The invoice is still reported to the FTA — reporting does not depend on the buyer being on the network.
Invoicing in a foreign currency
When no conversion is required. An invoice in AED needs nothing from you here. exchange_rate is ignored, and the XML carries no cbc:TaxCurrencyCode, no conversion rate and no second cac:TaxTotal — one tax total, in dirhams, which is already the currency the FTA assesses in. The one thing that is always there is cac:ItemPriceExtension per line, which is in AED on every invoice and is simply your own line amount when you invoice in AED.
When it is. Any currency other than AED — an export is just the common case — means the VAT has to be reported in dirhams as well as in yours. Send currency as you invoice it and the platform adds the AED side for you: the AED tax accounting currency (TaxCurrencyCode), the invoice total including VAT in AED, the conversion rate itself, and the per-line AED amounts. Your own totals stay in your currency — nothing you send is rewritten.
Where the rate comes from. exchange_rate is optional. Send one and it is used exactly as sent, always — it is your ledger, and we never re-price a rate you stated. Leave it out and we use the UAE Central Bank rate published for your invoice's own date, the rate the Central Bank publishes for VAT related obligations. It publishes Monday to Friday — nothing on Saturdays, Sundays or UAE public holidays — so a weekend invoice takes the last published business day's rate.
When the rate is refreshed. Twice, and never on a timer: once when you create the invoice, so the rate is on the record and visible in the validate response, and once more at the moment the invoice is converted and sent. Nothing polls the Central Bank in the background. The second look is what catches an invoice created early on its own invoice date, before that day's rates were published: at creation it took the previous business day's rate, and by the time it is sent the real one is used. A rate already published for a past date never changes, so nothing is re-fetched for it — and a rate you sent yourself is never part of any of this.
If no rate exists. A currency the Central Bank does not publish, or a date with nothing published anywhere near it — a date in the future, for instance — fails validation with the missing-rate error rather than being sent. The platform defaults a rate; it never invents one.
Optional export fields
customs_declaration_ref | Customs reference / export declaration number. Send it when your customs paperwork has one. |
incoterms | Incoterms for the shipment, e.g. "CIF", "FOB", "EXW". |
deliver_to_party_name | The party the goods are delivered to, when that is not the buyer you are billing (triangular sales). |
deliver_to_party_id | That party's TRN / TIN, with deliver_to_party_id_scheme for its scheme. |
actual_delivery_date | The date the goods or services were actually delivered, YYYY-MM-DD. |
A complete export payload
POST /einv/oauth/invoices
Authorization: Bearer $ACCESS_TOKEN
{
"entity_id": "REPLACE_WITH_ENTITY_ID",
"invoice_number": "EXP-1001",
"date": "2026-09-15",
"transaction_type_code": "00000001", // <- declares the export
"currency": "USD",
"exchange_rate": 3.6725, // <- optional: omit it and we use the
// UAE Central Bank rate for "date"
"payment_means_code": "30",
"payment_account_id": "AE070331234567890123456",
"buyer": {
"name": "Kuwait Retail LLC",
"country": "KW",
"address_line1": "12 Al Shuhada St",
"city": "Kuwait City",
"region": "Al Asimah"
// No "trn": an overseas buyer has none.
// If the buyer IS on the Peppol network, add instead:
// "participant_id": "7300010000001",
// "participant_scheme": "0088" // optional; follows from country.
// // Spelled out here because a GLN is
// // issued globally, not by the country.
// Otherwise the platform addresses it to 0235:9900000099.
},
"delivery": { // <- required on an export
"address_line1": "12 Al Shuhada St",
"city": "Kuwait City",
"country_subdivision": "Al Asimah",
"country": "KW",
"post_code": "13001"
},
"customs_declaration_ref": "CUSTOMS-AE-2026-8891", // optional
"incoterms": "CIF", // optional
"deliver_to_party_name": "Kuwait Retail LLC", // optional
"deliver_to_party_id": "300123456700003", // optional
"line_items": [
{
"description": "Consultancy services",
"quantity": 10,
"unit_amount": 100.0,
"tax_category": "Z", // <- zero rated
"tax_rate": 0,
"tax_amount": 0.0,
"unit": "EA"
}
]
}Run it through POST /oauth/invoices/validate first. The dry run applies exactly the same export rules and returns the same errors, but stores nothing and sends nothing — so your invoice number stays free for the real call.
Errors you will see if something is missing
Deliver to address line 1 (IBT-075), city (IBT-077) and country subdivision / state (IBT-079) are required for an export supply | Add the delivery block. It is mandatory once transaction_type_code declares an export. |
Deliver to country code (IBT-080) is required for the export delivery address | Add delivery.country as an ISO 3166-1 alpha-2 code. |
Currency exchange rate (BTUAE-04) is required when the invoice currency differs from AED | The Central Bank has published no rate for that currency on that date — a date in the future is the usual cause. Use a real invoice date, or send exchange_rate yourself. |
buyer.trn or buyer.participant_id is required for a UAE buyer | Only a UAE buyer has to identify itself. Check buyer.country: a buyer outside the UAE may omit both and is addressed by 0235:9900000099 without declaring anything, so this message means the country reads as the UAE (or is missing). If it really is a UAE buyer, send its TRN. |
Foreign (export) buyer requires a valid Peppol endpoint scheme (IBT-049-1) | You sent buyer.participant_id without a usable buyer.participant_scheme. A scheme you state is never replaced, so it has to be right: a real EAS code, and never "0235" for a non-UAE buyer. Sending only a tax number does NOT produce this — an address that cannot be derived takes the FTA placeholder instead. |
You send business data; the document that reaches Peppol carries a good deal more. These are every value the platform adds, derives or replaces — 40 in total — so you can see exactly what leaves on your behalf.
Under each value is what that element actually means — what the business term is for, and what its permitted values signify. The UBL code lists are spelled out there (invoice type, VAT category, payment means, units of measure, emirate codes, the eight transaction-type flags), so you can read a generated document without a UBL manual open beside it.
The Override column is the practical answer to “can I change this?”: you set it a request field sets it, from entity change it on the entity record, and fixed by us it is fixed or derived and anything you send is replaced.
| PINT XML path | Value & what it means | When / why | Override |
|---|---|---|---|
cbc:CustomizationID | urn:peppol:pint:billing-1@ae-1A URN naming the rulebook this document follows. pint:billing-1 is the international PINT Billing specification; @ae-1 is the UAE country extension of it. The receiver and the FTA read this to decide which set of validation rules to run — so it decides whether the document is judged against UAE rules at all. | Always. Declares the document as UAE PINT Billing. | fixed by us |
cbc:ProfileID | urn:peppol:bis:billingNames the Peppol business process the document takes part in: one-way billing — an invoice is sent, and no response document is expected back over the network. | Always. The Peppol business process. | fixed by us |
cbc:ProfileExecutionID | "00000000"BTUAE-02. Eight characters, each position a 0/1 flag, in this order: 1 free-trade zone, 2 deemed supply, 3 profit-margin scheme, 4 summary invoice, 5 continuous supply, 6 disclosed-agent billing, 7 e-commerce, 8 exports. "00000000" therefore means "an ordinary tax invoice, nothing special declared"; "00000001" declares an export; "01000000" declares a deemed supply. | Always. The BTUAE-02 transaction-type flags; your transaction_type_code replaces it. | you set ittransaction_type_code |
cbc:ID | A UUID we generate when the invoice record is createdIBT-001, the identifier a receiver quotes when referring back to this document. It has to be unique across everything you ever send, which is why we generate it rather than trust a per-series invoice number. | Always. This is the document identifier, NOT your invoice number — your invoice_number is what you search and reconcile by, and it is stored on the record. | fixed by us |
cbc:UUID | The same UUID as cbc:IDBTAE-07, a globally unique document identifier. The FTA uses it to recognise the same document if it ever arrives twice, so it must never be reused for a different invoice. | Always (BTAE-07). Emitted in both slots so the two can never disagree. | fixed by us |
cbc:InvoiceTypeCode | "380" (Tax Invoice)IBT-003, a UNCL 1001 code saying what kind of document this is. 380 is the code PINT-AE uses for a Tax Invoice. Its neighbours in the list are 381 credit note, 383 debit note and 389 self-billed invoice — none of which this API produces today. | Always — including on exports, which are Tax Invoices and never Commercial Invoices.invoice_type is accepted and validated but does NOT change this. Credit notes are not yet supported on this API — see OAuth-specific behaviour. | fixed by us |
cbc:DocumentCurrencyCode | "AED"IBT-005, an ISO 4217 three-letter code. It is the currency every amount on the invoice is expressed in — there is no per-line currency. AED is the UAE dirham. | Always. Your currency replaces it. | you set itcurrency |
cbc:TaxCurrencyCode | "AED"IBT-006, the currency VAT is accounted for in with the FTA. UAE VAT is always accounted in dirhams, so the value never varies. The rule requires it to differ from the document currency, which is exactly why an AED invoice carries no such element. | Only when currency is not AED (IBT-006, §12.2 item 16). Omitted entirely on an AED invoice. | fixed by us |
cbc:DueDate | OmittedIBT-009, the calendar date payment falls due, as YYYY-MM-DD. Leaving it out means "no due date stated" — it does not mean "due on receipt". | Emitted only when you send due_date. | you set itdue_date |
These describe the OAuth invoice API only. They are not statements about how Taxstar handles invoices generally, and they do not apply to invoices sent through a connected ERP.
Everything on this page is the OAuth invoice API only
Invoices that reach Taxstar through a connected ERP (Xero, QuickBooks, Zoho, Odoo, Naqood, Wafeq) are built from that entity's PINT mapping instead, and none of the defaults or rules below describe them. If you send through both, do not assume one behaves like the other.
The send is asynchronous
POST /oauth/invoices returns 202 as soon as the invoice is stored and queued. Validation, XML conversion and Peppol delivery all happen afterwards, so a 202 is not a successful send — poll the status endpoint, or register a webhook.
The seller party comes from the entity, never the request
The seller address, TRN and Peppol identity are read from the entity record at send time. The seller block on the request exists only to override the legal-registration values, and on this API even those are validated rather than emitted.
Credit notes are not supported yet
invoice_type accepts "Credit Note", but every document this API produces is a Tax Invoice (380). Do not use it to reverse an invoice — the buyer would receive a second tax invoice rather than a credit note. Raise credit notes through a connected ERP until this is available.
Validate first — it is free and identical
POST /oauth/invoices/validate runs the same rules the send runs, stores nothing and queues nothing. A payload that passes there passes at send time, and your invoice number stays free.
One request followed through: a domestic invoice with a single standard-rated line paid by credit transfer, the response it returns, and the document it produces. For the export equivalent see Export invoice.
POST /einv/oauth/invoices
Authorization: Bearer $ACCESS_TOKEN
Content-Type: application/json
{
"entity_id": "REPLACE_WITH_ENTITY_ID",
"invoice_number": "INV-1042",
"date": "2026-09-15",
"due_date": "2026-10-15",
"currency": "AED",
"payment_means_code": "30",
"payment_account_id": "AE070331234567890123456",
"buyer": {
"name": "Dubai Buyer LLC",
"country": "AE",
"trn": "100999888700003",
"address_line1": "Sheikh Zayed Road",
"city": "Dubai",
"region": "Dubai",
"email": "ap@buyer.example",
"legal_registration_id": "CN-1234567",
"legal_registration_type": "TL",
"authority_name": "Department of Economic Development - Dubai"
},
"line_items": [
{
"description": "Consulting services",
"quantity": 10,
"unit_amount": 100.0,
"tax_amount": 50.0,
"tax_rate": 5,
"tax_category": "S",
"unit": "EA"
}
]
}202 Accepted
{
"message": "Invoice received and queued for processing",
"invoice_id": "68f0aabbccddeeff00112233",
"status": "PROCESSING"
}
# The invoice is stored and queued. Validation, XML conversion and
# delivery follow asynchronously — poll the status endpoint:
GET /einv/oauth/invoices/{invoice_id}/status
{
"invoice_id": "68f0aabbccddeeff00112233",
"invoice_number": "INV-1042",
"direction": "sent",
"status": "SENT_TO_PEPPOL",
"category": "success",
"terminal": true
}The document that request produces. Every element not present in the payload was added by the platform — the comments point at the ones people ask about most.
<!-- What the request above becomes. Everything not shown in the
request body was added by the platform — see Default PINT XML values. -->
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" …>
<cbc:CustomizationID>urn:peppol:pint:billing-1@ae-1</cbc:CustomizationID>
<cbc:ProfileID>urn:peppol:bis:billing</cbc:ProfileID>
<cbc:ProfileExecutionID>00000000</cbc:ProfileExecutionID>
<cbc:ID>55555555-5555-5555-5555-555555555555</cbc:ID>
<cbc:UUID>55555555-5555-5555-5555-555555555555</cbc:UUID>
<cbc:IssueDate>2026-09-15</cbc:IssueDate>
<cbc:DueDate>2026-10-15</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>AED</cbc:DocumentCurrencyCode>
<cac:AccountingSupplierParty> <!-- from the entity record -->
<cac:Party>
<cbc:EndpointID schemeID="0235">1001234567</cbc:EndpointID>
<cac:PostalAddress>
<cbc:StreetName>Office 12</cbc:StreetName>
<cbc:CityName>Dubai</cbc:CityName>
<cbc:PostalZone>00000</cbc:PostalZone>
<cbc:CountrySubentity>DXB</cbc:CountrySubentity> <!-- "Dubai" -> code -->
<cac:Country><cbc:IdentificationCode>AE</cbc:IdentificationCode></cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>100123456700003</cbc:CompanyID>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Taxstar Trading FZ LLC</cbc:RegistrationName>
<cbc:CompanyID schemeID="0235" schemeAgencyID="PAS"
schemeAgencyName="AE">1001234567</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty> <!-- from your buyer block -->
<cac:Party>
<cbc:EndpointID schemeID="0235">1009998887</cbc:EndpointID> <!-- TRN[:10] -->
<cac:PostalAddress>
<cbc:StreetName>Sheikh Zayed Road</cbc:StreetName>
<cbc:CityName>Dubai</cbc:CityName>
<cbc:CountrySubentity>DXB</cbc:CountrySubentity>
<cac:Country><cbc:IdentificationCode>AE</cbc:IdentificationCode></cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>100999888700003</cbc:CompanyID>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Dubai Buyer LLC</cbc:RegistrationName>
<cbc:CompanyID>CN-1234567</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="Credit transfer">30</cbc:PaymentMeansCode>
<cac:PayeeFinancialAccount>
<cbc:ID>AE070331234567890123456</cbc:ID>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="AED">50.0</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="AED">1000.0</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="AED">50.0</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>5.0</cbc:Percent> <!-- forced from the category -->
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="AED">1000.0</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="AED">1000.0</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="AED">1050.0</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="AED">1050.0</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID> <!-- line position -->
<cbc:InvoicedQuantity unitCode="EA">10.0</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="AED">1000.0</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Consulting services</cbc:Description>
<cbc:Name>Consulting services</cbc:Name> <!-- falls back to description -->
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID><cbc:Percent>5.0</cbc:Percent>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="AED">100.0</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="EA">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
</Invoice>HTTP status codes
400 | The payload failed schema validation — a missing required field, a bad enum, or a conditional rule such as an export with no delivery block. The body carries a list of field errors. |
401 | Missing, expired or invalid access token. |
404 | The entity does not exist, or does not belong to your application. |
409 | An invoice with that invoice_number already exists for the entity. The existing invoice_id is returned with the error. |
422 | The payload was well formed but failed the PINT-AE rules. Run it through /oauth/invoices/validate to see every failure at once. |
500 / 502 | An internal or gateway failure. Retry; the invoice number is only consumed once an invoice is stored. |
Rule failures after a 202. A queued invoice can still fail validation or delivery. That does not come back on the HTTP response — it appears on the invoice's status trail as VALIDATION_FAILED or a delivery failure, with the failing rules in errors. Switch on category and stop when terminal is true.
Catch them before sending. POST /oauth/invoices/validate returns 200 whether or not the payload is valid; data.is_valid carries the verdict and data.errors lists every rule failure at once, each with the section it belongs to.
Next: the full API reference follows below — every endpoint, with its complete body-parameter table and a Try-It console.
/oauth/invoicesAccess tokenPush invoice
Create an invoice for one of your entities. Totals are computed from line items when omitted. Conditional rules: payment_account_id (IBAN) is required while payment_means_code is "30" (credit transfer); for registration type "TL" the issuing/authority name is required, for "PAS" the passport country is required instead. Registration types are the BTUAE-15/16 code list — "TL" (Commercial/Trade license), "EID" (Emirates ID), "PAS" (Passport) or "CD" (Cabinet decision) — and an AE seller on scheme "0235" must carry one of them, since ibr-173-ae rejects the document otherwise. The buyer address, buyer TRN, buyer legal registration and the payment means you send are emitted on the invoice delivered to Peppol (IBG-08, IBT-048, IBT-047/BTUAE-16/11, IBT-081/084); the seller party is built from the entity record, so keep its address and TRN up to date. The seller is identified to the network by the entity's registry code — emitted both as cbc:EndpointID and as cac:PartyLegalEntity/cbc:CompanyID (schemeID "0235", schemeAgencyID "PAS", schemeAgencyName "AE") — and by its full TRN as the IBT-031 VAT identifier. The entity TRN must therefore be a conformant UAE TRN (15 digits, starts "1", ends "03"). Validate a payload first with POST /oauth/invoices/validate to see exactly what the pipeline will accept.
Exporting outside the UAE? Set transaction_type_code to "00000001" — that is what declares the export, and it turns on the rest of the export rules. A foreign buyer has no UAE TRN, so send buyer.participant_id instead of buyer.trn; the invoice is delivered to that Peppol address exactly as given, never truncated. buyer.participant_scheme is optional — left out it is filled in from buyer.country ("0235" UAE, "0191" Estonia, otherwise that country's VAT scheme from the Peppol EAS codelist) — but send it for a globally-issued identifier such as a GLN, a DUNS or an LEI, which belongs to no country. A scheme you send must be a valid Peppol EAS code and, for a non-UAE buyer, can never be "0235". If the buyer is not on the Peppol network at all, omit both and the platform addresses the invoice to the FTA placeholder 0235:9900000099 — it is still reported to the FTA. An export additionally requires the delivery block. A non-AED currency does NOT require exchange_rate: send one and it is used as sent, omit it and the platform uses the UAE Central Bank rate published for the invoice's own date, refreshed once more when the invoice is sent. The full rules are in Export invoice details at the top of this page.
Dry-run an export payload — Validate an invoice{
"entity_id": "REPLACE_WITH_ENTITY_ID",
"invoice_number": "INV-1001",
"date": "2026-06-15",
"currency": "AED",
"invoice_type": "Tax Invoice",
"payment_means_code": "30",
"payment_account_id": "AE070331234567890123456",
"seller": {
"legal_registration_id": "2309778",
"registration_type": "TL",
"issuing_authority": "Department of Economic Development - Dubai"
},
"buyer": {
"name": "Buyer LLC",
"trn": "987654321098765",
"country": "United Arab Emirates",
"city": "Dubai",
"email": "ap@buyer.com",
"legal_registration_type": "TL",
"legal_registration_id": "CN-1234567",
"authority_name": "Department of Economic Development - Dubai"
},
"line_items": [
{
"description": "Consulting service",
"quantity": 10,
"unit_amount": 100,
"tax_amount": 50,
"tax_rate": 5,
"tax_category": "S",
"unit": "EA"
}
]
}Body parameters
entity_id | string | required | ID of the seller entity to invoice from. Must belong to your app — get it from GET /oauth/entities.In the PINT XML Not emitted. It selects the entity that fills the whole cac:AccountingSupplierParty block — seller endpoint, address, TRN and legal registration all come from it. |
invoice_number | string | required | Your unique invoice number. A duplicate number for the same entity is rejected with 409.In the PINT XML Not emitted. cbc:ID and cbc:UUID carry a UUID we generate. Your number is stored on the invoice record and is what you search and reconcile by. |
date | string | required | Invoice issue date, formatted YYYY-MM-DD.In the PINT XML → cbc:IssueDate (IBT-002). |
currency | string | optional | ISO 4217 currency code. Defaults to "AED".In the PINT XML → cbc:DocumentCurrencyCode (IBT-005), and the @currencyID on every amount in the document. |
invoice_type | string | optional | Document type. ACCEPTED BUT NOT YET HONOURED: every document this API produces is a Tax Invoice (InvoiceTypeCode 380), including exports, which are Tax Invoices by rule. Do not send "Credit Note" to reverse an invoice — the buyer would receive a second tax invoice rather than a credit note. Raise credit notes through a connected ERP until this is supported. See Default PINT XML values and OAuth-specific behaviour at the top of this page.In the PINT XML Not emitted. cbc:InvoiceTypeCode is always 380. |
due_date | string | optional | Payment due date, YYYY-MM-DD.In the PINT XML → cbc:DueDate (IBT-009). |
reference | string | optional | Free-text note shown on the invoice.In the PINT XML Not emitted. It is kept on the invoice record only — no cbc:Note or cbc:BuyerReference is produced. |
payment_means_code | string | optional | UNCL 4461 payment-means code. Defaults to "30". "30" = credit transfer, "10" = cash, "20" = cheque, "48" = bank card, "49" = direct debit.In the PINT XML → cac:PaymentMeans/cbc:PaymentMeansCode (IBT-081), with its UNCL 4461 label as @name. |
payment_account_id | string | conditional | Seller IBAN. Required (IBT-084) when payment_means_code is "30" (credit transfer); otherwise optional.In the PINT XML → cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID (IBT-084). |
payment_method | string | optional | Legacy human-readable label (e.g. "Credit Transfer"). Used only when payment_means_code is empty.In the PINT XML → the same cbc:PaymentMeansCode, once the label is translated to a code. Ignored when payment_means_code is sent. |
exchange_rate | number | optional | UAE Central Bank rate converting `currency` to AED. OPTIONAL: send one and it is used exactly as sent — it is your ledger, and a rate you state is never re-priced. Omit it and the platform uses the UAE Central Bank rate published for the invoice's own `date` (the last published business day when that date is a weekend or a UAE public holiday), refreshed once more at the moment the invoice is sent. Either way the platform then adds the AED tax accounting currency, the AED invoice total including VAT, the conversion rate and the per-line AED amounts for you. If the Central Bank has published no rate at all for that currency and date, the invoice fails validation — a rate is defaulted, never invented. Ignored for an AED invoice, which needs no conversion.In the PINT XML → cac:TaxExchangeRate/cbc:CalculationRate (BTUAE-04), and it computes four more AED figures: cac:AdditionalDocumentReference aedtotal-incl-vat (BTAE-20), the second cac:TaxTotal/cbc:TaxAmount @currencyID="AED" (IBT-111), and each line's cac:ItemPriceExtension amount and VAT (BTAE-08/10). |
transaction_type_code | string | optional | UAE transaction type — eight flag characters, defaulting to "00000000". In order: FreeTradeZone, DeemedSupply, MarginScheme, SummaryInvoice, ContinuousSupply, DisclosedAgent, Ecommerce, Exports. So "00000001" declares an EXPORT and turns on the export rules (placeholder buyer address, mandatory delivery block); "01000000" declares a deemed supply. Any other length, or a character that is not 0 or 1, is rejected.In the PINT XML → cbc:ProfileExecutionID (BTUAE-02). Position 8 also switches on the export rules, and position 2 re-addresses the buyer to 0235:9900000097. |
delivery | object | conditional | Deliver-to address — where the goods or services actually went. REQUIRED on an export (transaction_type_code "00000001"); optional otherwise, and omitted from the invoice entirely when you do not send it.In the PINT XML → cac:Delivery/cac:DeliveryLocation/cac:Address (IBG-15). |
delivery.address_line1 | string | conditional | Delivery street address (IBT-075). Required on an export.In the PINT XML → cac:Delivery/cac:DeliveryLocation/cac:Address/cbc:StreetName (IBT-075). |
delivery.city | string | conditional | Delivery city (IBT-077). Required on an export.In the PINT XML → …/cac:Address/cbc:CityName (IBT-077). |
delivery.country_subdivision | string | conditional | Delivery state / province / emirate (IBT-079). Required on an export. Also accepted as "region" or "state".In the PINT XML → …/cac:Address/cbc:CountrySubentity (IBT-079). |
delivery.country | string | conditional | Delivery country, ISO 3166-1 alpha-2 (IBT-080). Required on an export — for a genuine export this is the buyer's country, not AE.In the PINT XML → …/cac:Address/cac:Country/cbc:IdentificationCode (IBT-080). |
delivery.post_code | string | optional | Delivery postal code (IBT-078).In the PINT XML → …/cac:Address/cbc:PostalZone (IBT-078). |
delivery.address_line2 | string | optional | Additional delivery street line (IBT-076).In the PINT XML → …/cac:Address/cbc:AdditionalStreetName (IBT-076). |
delivery.address_line3 | string | optional | Further delivery address line (IBT-165).In the PINT XML → …/cac:Address/cac:AddressLine/cbc:Line (IBT-165). |
customs_declaration_ref | string | optional | Customs reference / export declaration number for the shipment. Export-only and optional — carried verbatim onto the invoice when you send it.In the PINT XML → cac:StatementDocumentReference/cbc:ID (BTAE-21), near the top of the document — not inside cac:Delivery. |
incoterms | string | optional | Incoterms for the shipment, e.g. "CIF", "FOB", "EXW". Export-only and optional.In the PINT XML → cac:Delivery/cac:DeliveryTerms/cbc:ID with @schemeID="Incoterms" (BTAE-22). |
deliver_to_party_name | string | optional | Name of the party the goods are delivered to, when that differs from the buyer you are billing (triangular sales).In the PINT XML → cac:Delivery/cac:DeliveryParty/cac:PartyName/cbc:Name (IBT-070). |
deliver_to_party_id | string | optional | TRN / TIN of that delivery party.In the PINT XML → cac:Delivery/cac:DeliveryLocation/cbc:ID (IBT-071). |
deliver_to_party_id_scheme | string | optional | Scheme for deliver_to_party_id, e.g. "0235" for a UAE TRN.In the PINT XML → the @schemeID on that cac:DeliveryLocation/cbc:ID (IBT-071-1). |
actual_delivery_date | string | optional | Date the goods or services were actually delivered, YYYY-MM-DD.In the PINT XML → cac:Delivery/cbc:ActualDeliveryDate (IBT-072). |
sub_total | number | optional | Taxable amount (sum of line net amounts). Computed from line_items when omitted.In the PINT XML → cac:LegalMonetaryTotal/cbc:LineExtensionAmount (IBT-106) and cbc:TaxExclusiveAmount (IBT-109). |
total_tax | number | optional | Total VAT (sum of line tax amounts). Computed from line_items when omitted.In the PINT XML → cac:TaxTotal/cbc:TaxAmount (IBT-110). |
total | number | optional | Grand total (sub_total + total_tax). Computed when omitted.In the PINT XML → cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount (IBT-112) and cbc:PayableAmount (IBT-115). |
seller | object | conditional | Seller legal-registration details — the identifier (IBT-030) plus its BTUAE-15 type and authority, mirroring the buyer.* legal-registration fields. Required when the seller electronic-ID scheme is "0235" and the entity itself carries no legal_registration_type — register it once on the entity (POST/PATCH /oauth/entities) and you can omit this block entirely. When both are present this block wins, for that invoice only.In the PINT XML Not emitted. The whole seller party is built from the entity record; this block is validated only. |
seller.legal_registration_id | string | conditional | The seller's legal registration number (IBT-030). Accepted and validated, but NO LONGER EMITTED on invoices sent through this API. The seller's Peppol identity is what goes into cac:PartyLegalEntity/cbc:CompanyID on a sent invoice: the entity's registry code, qualified schemeID="0235", schemeAgencyID="PAS", schemeAgencyName="AE". Billberry resolves the sending organization from that element, and a document carrying anything else there is rejected with 403 "Invoice Not From A Partner's Organization". Send it if you want the value validated; it does not change the emitted XML. The buyer counterpart, buyer.legal_registration_id, is unaffected and is still emitted as the buyer's IBT-047.In the PINT XML Not emitted. cac:PartyLegalEntity/cbc:CompanyID carries the entity's registry code, which the gateway routes on. |
seller.registration_type | string | conditional | BTUAE-15 registration type. One of "TL" = Commercial/Trade license, "EID" = Emirates ID, "PAS" = Passport, "CD" = Cabinet decision — any other value is rejected. NOTE: on invoices sent through this API the @schemeAgencyID on the seller legal registration identifier is fixed to "PAS" (with @schemeAgencyName "AE") so that ibr-173-ae is satisfied for the Peppol identity described under seller.legal_registration_id, so this field no longer changes the emitted XML. It is still validated, and it still applies to the ERP-driven flows.In the PINT XML Not emitted. @schemeAgencyID is fixed to "PAS" on this API. |
seller.issuing_authority | string | conditional | Authority that issued the registration (BTUAE-12). Required when seller.registration_type is "TL". NOTE: not emitted on invoices sent through this API — the @schemeAgencyName on the seller legal registration identifier is fixed to "AE" (see seller.legal_registration_id). Still validated, and still emitted by the ERP-driven flows.In the PINT XML Not emitted. @schemeAgencyName is fixed to "AE" on this API. |
seller.passport_country | string | conditional | Passport-issuing country (BTUAE-18), emitted as @schemeAgencyName. Required when seller.registration_type is "PAS".In the PINT XML Not emitted. Same @schemeAgencyName, fixed to "AE". |
buyer | object | required | Buyer (customer) details.In the PINT XML → cac:AccountingCustomerParty/cac:Party (IBG-07). |
buyer.name | string | required | Buyer legal name.In the PINT XML → cac:PartyLegalEntity/cbc:RegistrationName (IBT-044). |
buyer.trn | string | one of | Buyer tax number. For a UAE buyer this is the 15-digit TRN and it is required; its first 10 digits (the UAE TIN) become the buyer's Peppol address under scheme 0235. A FOREIGN buyer's own tax number is accepted here too and is used: the address is built from it under that country's VAT scheme from the Peppol EAS codelist, with the WHOLE identifier — the first-10 truncation is UAE-only. Send buyer.participant_id instead when you have one; it wins. One of the two is required for a UAE buyer. A buyer OUTSIDE the UAE may omit both, whatever transaction_type_code says: it cannot hold a UAE TRN, so it is treated as an export and addressed by the FTA placeholder 0235:9900000099. An invoice declaring a deemed supply may omit both as well.In the PINT XML → cac:PartyTaxScheme/cbc:CompanyID in full (IBT-048), and its first 10 digits also become cbc:EndpointID under scheme 0235 (IBT-049). |
buyer.participant_id | string | one of | Buyer Peppol participant identifier, for an EXPORT buyer that has no UAE TRN — the invoice is delivered to this address as-is, never truncated. Send it without the scheme prefix ("7300010000001"), optionally with buyer.participant_scheme, or in the combined form "0088:7300010000001". The scheme is optional and follows from buyer.country when omitted — state it when the identifier comes from a global registry (GLN, DUNS, LEI) rather than from the country itself. Its length follows the scheme, not a fixed rule — 10 digits for a UAE registry code, 8 for an Estonian one, 13 for a GLN. Takes precedence over buyer.trn when both are sent. Omit it when the export buyer is not on the Peppol network at all: the invoice is then addressed to 0235:9900000099 and still reported to the FTA.In the PINT XML → cbc:EndpointID (IBT-049), verbatim and never truncated. |
buyer.participant_scheme | string | optional | Peppol EAS scheme code for buyer.participant_id, e.g. "0088" (GLN). Optional: left out, it is filled in from buyer.country — "0235" for the UAE, "0191" for Estonia, otherwise that country's VAT scheme from the Peppol EAS codelist. Send it when the identifier comes from a GLOBAL registry rather than the buyer's country — a GLN ("0088"), a DUNS ("0060") or an LEI ("0199") is not tied to a country, so the country default would address a participant that does not exist. A scheme you send is used exactly as sent; it must be a valid EAS code, and for a non-UAE buyer it can never be "0235". Not needed at all when it is already part of the combined "0088:7300010000001" form.In the PINT XML → the @schemeID on cbc:EndpointID (IBT-049-1). |
buyer.country | string | required | Buyer country — name or ISO 3166-1 alpha-2 (e.g. "AE").In the PINT XML → cac:PostalAddress/cac:Country/cbc:IdentificationCode (IBT-055). |
buyer.address_line1 | string | optional | Buyer street address.In the PINT XML → cac:PostalAddress/cbc:StreetName (IBT-050). |
buyer.city | string | optional | Buyer city.In the PINT XML → cac:PostalAddress/cbc:CityName (IBT-052). |
buyer.region | string | optional | Buyer emirate / state.In the PINT XML → cac:PostalAddress/cbc:CountrySubentity (IBT-054), as the emirate code for a UAE buyer. |
buyer.email | string | optional | Buyer accounts-payable email.In the PINT XML Not emitted. No cac:Contact is produced; it is stored on the record and used for the optional PDF email. |
buyer.phone | string | optional | Buyer phone number.In the PINT XML Not emitted. No cac:Contact is produced. |
buyer.electronic_address | string | optional | Buyer Peppol electronic address.In the PINT XML Not emitted. The buyer's Peppol address comes from participant_id or the TRN. |
buyer.legal_registration_id | string | optional | Buyer legal registration ID (IBT-047), e.g. trade-license number.In the PINT XML → cac:PartyLegalEntity/cbc:CompanyID (IBT-047). |
buyer.legal_registration_type | string | optional | BTUAE-16 registration type, emitted as @schemeAgencyID on the buyer legal registration identifier. Same code list as the seller: "TL" = Commercial/Trade license, "EID" = Emirates ID, "PAS" = Passport, "CD" = Cabinet decision. Any other value is rejected; leave it empty for a buyer with no legal registration (e.g. a foreign buyer).In the PINT XML → the @schemeAgencyID on that cbc:CompanyID (BTUAE-16). |
buyer.authority_name | string | conditional | Authority that issued the registration (BTUAE-11), emitted as @schemeAgencyName. Required when buyer.legal_registration_type is "TL"; also emitted for "EID" and "CD" when you send it.In the PINT XML → the @schemeAgencyName on that cbc:CompanyID (BTUAE-11). |
buyer.passport_country_code | string | conditional | Passport-issuing country code (BTUAE-19), emitted as @schemeAgencyName. Required when buyer.legal_registration_type is "PAS".In the PINT XML → the same @schemeAgencyName, in place of authority_name, when the type is "PAS" (BTUAE-19). |
line_items | array | required | Invoice line items. At least one is required.In the PINT XML → one cac:InvoiceLine each (IBG-25), numbered cbc:ID 1, 2, 3 … in the order you send them. |
line_items[].description | string | required | Line description.In the PINT XML → cac:Item/cbc:Description (IBT-154) AND cac:Item/cbc:Name (IBT-153) — the same text in both. |
line_items[].quantity | number | required | Quantity.In the PINT XML → cbc:InvoicedQuantity (IBT-129). |
line_items[].unit_amount | number | required | Net unit price (must be > 0).In the PINT XML → cac:Price/cbc:PriceAmount (IBT-146). |
line_items[].tax_amount | number | required | VAT amount for this line.In the PINT XML → the cac:TaxSubtotal/cbc:TaxAmount for its category (IBT-117) and the document cac:TaxTotal; there is no per-line VAT element in PINT. |
line_items[].line_amount | number | optional | Net line amount. Computed as quantity × unit_amount when omitted.In the PINT XML → cbc:LineExtensionAmount (IBT-131). |
line_items[].tax_rate | number | optional | VAT percentage, e.g. 5.In the PINT XML → cbc:Percent (IBT-152) in cac:ClassifiedTaxCategory and cac:TaxSubtotal — but forced from the category, so what you send here is replaced. |
line_items[].tax_category | string | optional | Tax category code: S (standard), Z (zero), E (exempt), O (out of scope), AE (reverse charge). Defaults to "S". A qualifying EXPORT line is zero rated — send "Z" with tax_rate 0 and tax_amount 0. Do not assume every export is zero rated: state the category the supply actually attracts.In the PINT XML → cbc:ID (IBT-151) in cac:ClassifiedTaxCategory and in the cac:TaxSubtotal it is grouped into. |
line_items[].unit | string | optional | Unit of measure (IBT-130). Send a UN/ECE Rec 20 code ("HUR", "KGM") or a plain label ("Hour", "kg") — labels are translated to the code. Defaults to "EA" (each), and anything that cannot be translated falls back to "EA" rather than emitting a code the gateway would reject.In the PINT XML → the @unitCode on cbc:InvoicedQuantity (IBT-130) and on cac:Price/cbc:BaseQuantity, translated to its Rec 20 code. |
line_items[].discount_percent | number | optional | Line discount percentage.In the PINT XML Not emitted. The line allowance is computed as quantity × unit_amount − line_amount; this percentage is not read. Send the discounted line_amount. |
line_items[].account_code | string | optional | Accounting code. Defaults to "200".In the PINT XML Not emitted. It is stored on the record for your own reconciliation. |
line_items[].item_name | string | optional | Item name (IBT-153). ACCEPTED BUT NOT YET EMITTED on this API — the item name in the generated XML always repeats line_items[].description. Put the name you want the buyer to see in description until this is fixed.In the PINT XML Not emitted. cac:Item/cbc:Name repeats the description instead — see the note in this row's description. |
line_items[].item_type | string | optional | Item type.In the PINT XML Not emitted. |
line_items[].hs_code | string | optional | HS commodity code (for goods).In the PINT XML Not emitted. There is no PINT-AE element for it on this API. |
line_items[].service_code | string | optional | Service classification code (for services).In the PINT XML Not emitted. There is no PINT-AE element for it on this API. |
/oauth/invoices/bulkAccess tokenPush multiple invoices
Send multiple invoices in a single request. Each invoice is processed independently — one failure does not block the others. Returns HTTP 207 Multi-Status with a per-item results array containing the invoice_number, status_code, and the same fields as the single-invoice response.
{
"invoices": [
{
"entity_id": "REPLACE_WITH_ENTITY_ID",
"invoice_number": "INV-1001",
"date": "2026-06-15",
"currency": "AED",
"invoice_type": "Tax Invoice",
"payment_means_code": "30",
"payment_account_id": "AE070331234567890123456",
"seller": {
"legal_registration_id": "2309778",
"registration_type": "TL",
"issuing_authority": "Department of Economic Development - Dubai"
},
"buyer": {
"name": "Buyer LLC",
"trn": "987654321098765",
"country": "United Arab Emirates",
"city": "Dubai",
"email": "ap@buyer.com",
"legal_registration_type": "TL",
"legal_registration_id": "CN-1234567",
"authority_name": "Department of Economic Development - Dubai"
},
"line_items": [
{
"description": "Consulting service",
"quantity": 10,
"unit_amount": 100,
"tax_amount": 50,
"tax_rate": 5,
"tax_category": "S",
"unit": "EA"
}
]
}
]
}Body parameters
invoices | array | required | List of invoices to send. At least one is required. Each item follows the same schema as POST /oauth/invoices — see Push invoice for the full field reference.In the PINT XML → one complete PINT XML document per entry; the entries are independent and one failing does not stop the rest. |
invoices[].entity_id | string | required | ID of the seller entity. Must belong to your app.In the PINT XML → selects the entity behind cac:AccountingSupplierParty for that document. |
invoices[].invoice_number | string | required | Unique invoice number per entity. A duplicate is rejected with status_code 409 in the results.In the PINT XML Not emitted. cbc:ID and cbc:UUID carry a UUID we generate. |
invoices[].date | string | required | Invoice issue date, YYYY-MM-DD.In the PINT XML → cbc:IssueDate (IBT-002). |
invoices[].buyer | object | required | Buyer details. See Push invoice for the full buyer field list.In the PINT XML → cac:AccountingCustomerParty/cac:Party (IBG-07). |
invoices[].line_items | array | required | Invoice line items. At least one required per invoice.In the PINT XML → one cac:InvoiceLine each (IBG-25). |
/oauth/invoices/validateAccess tokenValidate an invoice (dry run)
Check an invoice payload against the UAE PINT-AE rules WITHOUT storing or sending anything — the very same validation the send pipeline runs before it converts an invoice to XML and delivers it to Peppol, so a payload that passes here passes at send time. Use it before Push invoice to confirm the data is correct. The response is 200 whether or not the data is valid: `data.is_valid` carries the verdict, `data.total_errors` / `data.errors` list every rule failure (each with a `section` and a `message`), and `data.warnings` reports anything that is not a rule failure but would still make the send fail — currently a duplicate invoice number for the entity. Nothing is persisted and no invoice is queued, so the invoice number stays free and the same payload can be validated as often as you like. The check covers the seller party built from your entity record (IBG-05 postal address, IBT-031 TRN — which must be a conformant UAE TRN, and is reported as a seller_party error when it is not — and the IBT-030 Peppol identity), the buyer party you send (IBG-08 postal address, IBT-048 TRN, IBT-047/BTUAE-16 legal registration and BTUAE-11 authority name), the payment means (IBT-081/084), the document totals and every line. It also enforces ibr-173-ae, the fatal rule behind most gateway rejections: an AE seller on electronic-ID scheme "0235" must qualify its legal registration identifier with a BTUAE-15 type of "TL", "EID", "PAS" or "CD".
The request body is exactly the body of POST /oauth/invoices — same schema, same defaults, same conditional rules, and the same export rules (declare the export with transaction_type_code "00000001" and the dry run applies everything an export requires). The table below lists only the identifying fields; for the full Body parameters reference (every seller, buyer, delivery, payment and line-item field, with its required/conditional/optional status) see Push invoice.
Body parameters — Push invoice{
"entity_id": "REPLACE_WITH_ENTITY_ID",
"invoice_number": "INV-1001",
"date": "2026-06-15",
"currency": "AED",
"invoice_type": "Tax Invoice",
"payment_means_code": "30",
"payment_account_id": "AE070331234567890123456",
"seller": {
"legal_registration_id": "2309778",
"registration_type": "TL",
"issuing_authority": "Department of Economic Development - Dubai"
},
"buyer": {
"name": "Buyer LLC",
"trn": "987654321098765",
"country": "United Arab Emirates",
"city": "Dubai",
"email": "ap@buyer.com",
"legal_registration_type": "TL",
"legal_registration_id": "CN-1234567",
"authority_name": "Department of Economic Development - Dubai"
},
"line_items": [
{
"description": "Consulting service",
"quantity": 10,
"unit_amount": 100,
"tax_amount": 50,
"tax_rate": 5,
"tax_category": "S",
"unit": "EA"
}
]
}Body parameters
entity_id | string | required | ID of the seller entity the invoice would be sent from. Must belong to your app, otherwise 404.In the PINT XML → selects the entity behind cac:AccountingSupplierParty. |
invoice_number | string | required | The invoice number you intend to send. Not reserved by this call — if it is already used for the entity you get a warning, not an error.In the PINT XML Not emitted. cbc:ID and cbc:UUID carry a UUID we generate. |
date | string | required | Invoice issue date, YYYY-MM-DD.In the PINT XML → cbc:IssueDate (IBT-002). |
buyer | object | required | Buyer details. Same schema as Push invoice — see Push invoice for the full buyer field list.In the PINT XML → cac:AccountingCustomerParty/cac:Party (IBG-07). |
line_items | array | required | Invoice line items. At least one is required. Same schema as Push invoice — see Push invoice for the full line-item field list.In the PINT XML → one cac:InvoiceLine each (IBG-25). |
(all other fields) | — | optional | See the full Body parameters table under Push invoice (POST /oauth/invoices) — every other field, from currency and seller.registration_type to payment_means_code, the rest of buyer, the rest of line_items and the totals, follows exactly the same schema, defaults and conditional rules here.In the PINT XML Each maps exactly as it does on Push invoice — see the In the PINT XML line on every row of that table. |
/oauth/invoices/{invoice_id}Access tokenUpdate & resend an invoice
Update the data of an invoice and re-queue it for processing (validate → XML → deliver to Peppol). Only allowed while the invoice has NOT been delivered to the Peppol network — i.e. it is still queued or it failed at validation, XML conversion or dispatch. An invoice that was already sent to Peppol is immutable and returns 409. The body uses the SAME schema as Push invoice (a full invoice payload, not a partial patch) and is validated with the same rules. The identifying fields entity_id and invoice_number cannot be changed — they must match the values the invoice was created with, otherwise the request is rejected with 400. On success the invoice status becomes PROCESSING and it is queued again for sending.
Path parameters
invoice_id | Invoice id (the `_id` returned by Push invoice / List sent invoices). |
{
"entity_id": "SAME_ENTITY_ID_AS_CREATED",
"invoice_number": "INV-1001",
"date": "2026-06-15",
"currency": "AED",
"invoice_type": "Tax Invoice",
"payment_means_code": "30",
"payment_account_id": "AE070331234567890123456",
"seller": {
"legal_registration_id": "2309778",
"registration_type": "TL",
"issuing_authority": "Department of Economic Development - Dubai"
},
"buyer": {
"name": "Buyer LLC",
"trn": "987654321098765",
"country": "United Arab Emirates",
"city": "Dubai",
"email": "ap@buyer.com",
"legal_registration_type": "TL",
"legal_registration_id": "CN-1234567",
"authority_name": "Department of Economic Development - Dubai"
},
"line_items": [
{
"description": "Consulting service",
"quantity": 10,
"unit_amount": 100,
"tax_amount": 50,
"tax_rate": 5,
"tax_category": "S",
"unit": "EA"
}
]
}Body parameters
entity_id | string | required | Immutable — must be the same entity_id the invoice was created with. Sending a different value returns 400.In the PINT XML → selects the entity behind cac:AccountingSupplierParty. |
invoice_number | string | required | Immutable — must be the same invoice_number the invoice was created with. Sending a different value returns 400.In the PINT XML Not emitted. cbc:ID and cbc:UUID carry a UUID we generate. |
date | string | required | Invoice issue date, YYYY-MM-DD.In the PINT XML → cbc:IssueDate (IBT-002). |
buyer | object | required | Buyer details. Same schema as Push invoice — see Push invoice for the full buyer field list.In the PINT XML → cac:AccountingCustomerParty/cac:Party (IBG-07). |
line_items | array | required | Invoice line items. At least one is required. Same schema as Push invoice — see Push invoice for the full line-item field list.In the PINT XML → one cac:InvoiceLine each (IBG-25). |
(all other fields) | — | optional | Every other field (currency, seller, payment_means_code, totals, …) follows exactly the same schema, defaults and conditional rules as POST /oauth/invoices — see Push invoice for the full field reference.In the PINT XML Each maps exactly as it does on Push invoice — see the In the PINT XML line on every row of that table. |
/oauth/invoices/sentAccess tokenList sent invoices
Return invoices SENT by your application. An app can own many users and entities, so narrow the results with entity_id and/or user_id.
Query parameters
entity_id | Filter to invoices sent from a single entity (its id from GET /oauth/entities). |
user_id | Filter to invoices owned by a single user (its id from GET /oauth/users). |
page | Page number. Defaults to 1. |
limit | Page size, 1–100. Defaults to 20. |
/oauth/invoices/sent/{invoice_id}Access tokenGet a sent invoice
Fetch a single sent invoice (including its full status history).
/oauth/invoices/receivedAccess tokenList received invoices
Return invoices RECEIVED over the Peppol network for your entities (delivered via Billberry). Read-only. An app can own many users and entities, so narrow the results with entity_id and/or user_id.
Query parameters
entity_id | Filter to invoices received by a single entity (its id from GET /oauth/entities). |
user_id | Filter to entities owned by a single user (its id from GET /oauth/users). |
page | Page number. Defaults to 1. |
limit | Page size, 1–100. Defaults to 20. |