Officially Approved UAE E-Invoicing Service Provider — TronStride FZC
🇴🇲2026 — Oman E-Invoicing Journey Started
UAE Peppol · PINT-AE v1.04 · FTA compliant

Aigentrix E-Invoice API

Create, validate, submit and track UAE Peppol e-invoices from your ERP or accounting system. One API key, no JWT to manage. Every code list and rule id on this page comes from the production PINT-AE v1.04 ruleset.

Base URLhttps://app.aigentrix.ai/external/api/v1
Auth headerX-API-KEY
Endpoints29
Updated6 September 2026

Getting started

Six steps from zero to a delivered invoice. If you already have an API key and a company id, skip to the quick start below.

Quick start — create, then submit

bash
# 1. Create the invoice — it is stored as DRAFT, nothing is sent yet
curl -X POST "https://app.aigentrix.ai/external/api/v1/eInvoiceEntry/createFull" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: $AIGENTRIX_API_KEY" \
  -d '{
    "companyId": 20,
    "documentId": "INV-2026-001",
    "issueDate": "2026-09-06",
    "invoiceTypeCode": "380",
    "invoiceTransactionType": "00000000",
    "documentCurrencyCode": "AED",
    "paymentDueDate": "2026-10-06",
    "sellerRegisteredName": "ABC Trading LLC",
    "sellerVatTrn": "100123456700003",
    "sellerAddressLine1": "Sheikh Zayed Road",
    "sellerCity": "Dubai",
    "sellerCountrySubdivision": "DXB",
    "sellerCountryCode": "AE",
    "buyerRegisteredName": "XYZ Corp LLC",
    "buyerVatTrn": "100987654300003",
    "buyerAddressLine1": "Al Wahda Street",
    "buyerCity": "Sharjah",
    "buyerCountrySubdivision": "SHJ",
    "buyerCountryCode": "AE",
    "lineExtensionTotal": 1000.00,
    "taxAmount": 50.00,
    "totalIncludingTax": 1050.00,
    "payableAmount": 1050.00,
    "lines": [{
      "lineNumber": 1,
      "itemName": "Consulting services",
      "quantity": 10, "quantityUom": "HUR",
      "unitPrice": 100.00, "lineNetAmount": 1000.00,
      "taxCategory": "S", "taxRatePercent": 5,
      "taxScheme": "VAT", "lineTaxAmount": 50.00,
      "inclVatAmount": 1050.00
    }],
    "allowances": [],
    "payments": [{ "paymentMeansCode": "30" }],
    "terms": []
  }'
# → { "success": true, "status": "CREATED", "entryId": 45426, "documentId": "INV-2026-001" }

# 2. Submit it
curl -X PUT "https://app.aigentrix.ai/external/api/v1/eInvoiceEntry/45426" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: $AIGENTRIX_API_KEY" \
  -d '{ "id": 45426, "status": "SUBMITTED" }'

# 3. Check what happened
curl "https://app.aigentrix.ai/external/api/v1/eInvoiceEntry/45426/validationErrors" \
  -H "X-API-KEY: $AIGENTRIX_API_KEY"
1

👤

Create your Aigentrix account

Sign up on the Aigentrix platform. You need a valid business email address.

  1. 1Go to https://app.aigentrix.ai and click Get Started.
  2. 2Enter your full name, business email and a secure password.
  3. 3Verify your email address using the confirmation link.
  4. 4Log in with your credentials.
💡

If your organisation already has an account, ask your admin to invite you rather than creating a second organisation — an API key is scoped to exactly one organisation.

2

🏢

Create your organisation

An organisation is the top-level entity holding your companies, users and subscription.

  1. 1Go to Settings → Organisation.
  2. 2Click "Create Organisation" and enter your legal organisation name.
  3. 3Set the default country and currency.
  4. 4Click "Save".
💡

You never pass an organisationId to the API — it is resolved server-side from your API key.

3

🏭

Add a company

A company is the legal entity that issues or receives e-invoices. Its master record supplies the seller defaults for every invoice you create.

  1. 1Go to Settings → Companies → Add Company.
  2. 2Enter the registered name, the VAT TRN (15 digits, starts with 1, ends with 03) and the trade licence number.
  3. 3Set the country to AE, the emirate (AUH / DXB / SHJ / AJM / UAQ / RAK / FUJ) and the currency to AED.
  4. 4Click "Save" and note the Company ID — this is companyId in every API call.
💡

Blank seller fields in a request are filled from this record, so keep the legal registration id, type and authority complete — otherwise documents fail rules ibr-150-ae, ibr-181-ae and ibr-172-ae.

4

📄

Enable e-invoicing for the company

Activate the e-invoice module so the company can submit, validate and track Peppol documents.

  1. 1Open the company record and go to the E-Invoice tab.
  2. 2Toggle "Enable E-Invoice" to ON.
  3. 3Set the default environment — SANDBOX or LIVE. This becomes einvoiceDefaultEnv.
  4. 4The Peppol participant id is derived as 0235: plus your TIN. Override it only if you have been told to.
  5. 5Click "Save Configuration".
💡

SANDBOX routes to the Peppol TEST network and LIVE to production. Both use the same API, the same rules and the same validation.

5

🔑

Generate an API key

API keys let your ERP or scripts call Aigentrix without user login tokens. There is no JWT to manage.

  1. 1Go to Settings → API Keys → Generate New Key.
  2. 2Give the key a descriptive label, for example ERP Integration – Production.
  3. 3Click "Generate" and copy the key immediately — it is shown only once.
  4. 4Store it securely, for example as AIGENTRIX_API_KEY in your environment.
💡

Send the key as the X-API-KEY header on every request. A missing, invalid or revoked key returns 401. Keys can be revoked at any time.

6

🚀

Make your first call

List your companies, create a draft invoice, then submit it. Three requests, end to end.

  1. 1GET /companies — note the id in the response, this is your companyId.
  2. 2POST /eInvoiceEntry/createFull — the entry is stored as DRAFT and nothing is sent yet.
  3. 3PUT /eInvoiceEntry/{entryId} with { "id": …, "status": "SUBMITTED" } — exactly what the Submit button does.
  4. 4GET /eInvoiceEntry/{entryId} to follow the status, or subscribe to webhooks instead of polling.
  5. 5If it fails, GET /eInvoiceEntry/{entryId}/validationErrors returns the failed rule ids.
💡

Want to try before writing anything? POST to /eInvoiceEntry/validate — it runs the full XSD and Schematron validation with no database write.

Authentication and conventions

Every request carries your organisation's API key. A key belongs to exactly one organisation, and the companyId you send must be one of that organisation's companies.

Missing key401
No X-API-KEY header
Invalid or revoked key401
Key not recognised
Wrong organisation403
{ "errorKey": "Access denied" }
Malformed request400
{ "error": "…" }

Environments

environment=SANDBOX (the default) routes to the Peppol test network; environment=LIVE routes to production. Both use the same API, the same rules and the same validation. The company's own default is einvoiceDefaultEnv.

Data conventions

TopicRule
Datesyyyy-MM-dd. A date-time is accepted and the time part is dropped.
AmountsAt most 2 decimals. The exchange rate takes at most 6.
StringsTrimmed server-side. A blank string is treated as not provided.
Country codesISO 3166-1 alpha-2, e.g. AE, SE.
Country subdivisionFor AE addresses one of AUH DXB SHJ AJM UAQ RAK FUJ; free text elsewhere.
Currency codesISO 4217, e.g. AED, USD.
Unit of measureUN/ECE Rec 20, e.g. EA, H87, HUR, KGM, C62.
Unknown JSON keysIgnored silently — a typo in a field name drops the value. Always check the read-back.
javascript
// Node.js / browser
const response = await fetch(
  "https://app.aigentrix.ai/external/api/v1/eInvoiceEntry/createFull",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-KEY": process.env.AIGENTRIX_API_KEY,
    },
    body: JSON.stringify(invoicePayload),
  }
);

const data = await response.json();
if (!data.success) {
  // A business error still arrives with HTTP 200 — check the flag.
  throw new Error(data.errorKey ?? "createFull failed");
}
console.log(data.entryId);

Document lifecycle and statuses

Two independent status fields: status tracks delivery over the Peppol network, taxStatus tracks reporting to the FTA.

status — the happy path

DRAFT

Stored, not submitted

SUBMITTED

Queued for validation

PROCESSING

Being converted

VALIDATION_PASSED

XSD + Schematron passed

SENDING

Handed to the access point

DELIVERED

Accepted by the receiver

ACKNOWLEDGED

Business acknowledgement received

Everything else

VALIDATION_FAILEDRejected by the rules — read validationErrors, fix and resubmit. Editable.
REJECTEDRejected by the receiver or the FTA. Editable.
ERROR / TRANSMISSION_FAILEDTransport failure. Retried automatically, then escalated. Editable.
CREDIT_NOTE_ISSUEDA credit note references this invoice. User fields only.
RECEIVEDAn inbound document from a supplier.
RESPONSE_GENERATEDAn application response was produced.

Editable vs locked. DRAFT, VALIDATION_FAILED, REJECTED and TRANSMISSION_FAILED accept a full update. At every other status only userField01userField10 can change.

taxStatus — FTA reporting

Independent of the delivery status above. An invoice can be DELIVERED on the network while its tax reporting is still in progress.

text
NOT_INITIATED
   → REPORTING_INITIATED
       → REPORTING_CONFIRMED  /  TDD_VALIDATION_PASSED  |  TDD_VALIDATION_FAILED
           → ACKNOWLEDGED  |  REJECTED

Withdrawal path:  WITHDRAW_INITIATED → WITHDRAWN

API reference

All 29 endpoints sit under /external/api/v1 and require the X-API-KEY header. Path parameters shown in braces must be replaced with real values.

Try it in Postman

The collection carries every endpoint plus nine verified scenario requests and seven example webhook payloads, with the environment variables pre-configured.

Download collection
GETPOSTPUTDELETE

1Companies

Find the companyId you pass to every other call. No organisationId is needed — it is resolved from your API key.

1.1GEThttps://app.aigentrix.ai/external/api/v1/companies

List companies

Paginated list of the companies your API key can access. The id in each row is the companyId used everywhere else.

Parameters

pageopt

Zero-based page number. Default 0.

perPageopt

Page size. Default 10.

sortByopt

Field to sort by. Default createdAt.

orderByopt

asc or desc. Default desc.

Notes

Start here — everything else needs the companyId this returns.
viaPeppol tells you whether the company is enabled for network delivery; isInvoice whether the e-invoice module is on.
json
{
  "totalRecords": 1,
  "companies": [
    {
      "id": 20,
      "code": "COMP001",
      "nameEN": "Sample Trading LLC",
      "cityEN": "Dubai",
      "email": "finance@sampletrading.ae",
      "taxVatRegistrationNumberEN": "100123456700003",
      "viaPeppol": true,
      "isInvoice": true
    }
  ]
}
1.2GEThttps://app.aigentrix.ai/external/api/v1/companies/{companyId}

Get company by ID

One company, including its TRN, TIN and Peppol participant id. The company is verified server-side to belong to your key's organisation.

Parameters

companyIdreq

The company id from 1.1.

Notes

A company that exists but belongs to another organisation returns errorKey "company.not.accessible".
json
{
  "id": 20,
  "code": "COMP001",
  "nameEN": "Sample Trading LLC",
  "cityEN": "Dubai",
  "email": "finance@sampletrading.ae",
  "taxVatRegistrationNumberEN": "100123456700003",
  "viaPeppol": true,
  "isInvoice": true
}

2Create an invoice

One JSON request creates one invoice. The entry is stored as DRAFT; submitting is a separate call.

2.1POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/createFull

Create e-invoice entry (full JSON)

Creates one invoice from JSON. The entry is stored as DRAFT and nothing is sent until you submit it. Every accepted key is listed in the field reference below.

Content-Type: application/json

Notes

Tax is computed once per tax breakdown (category + rate) on the summed net, not per line. With many lines the two differ — put the difference in roundingAmount.
Re-posting the same documentId for the same company overwrites an existing DRAFT or failed entry and returns status OVERWRITTEN.
Unknown JSON keys are ignored silently, so a typo in a field name drops the value without an error. Read the entry back to confirm what landed.
Strings are trimmed server-side and a blank string is treated as not provided.
json
{
  "companyId": 20,
  "invoiceRef": "IIL-1001309",

  "documentId":                "IIL-1001309",
  "issueDate":                 "2026-07-14",
  "invoiceTypeCode":           "380",
  "invoiceTransactionType":    "00000000",
  "documentCurrencyCode":      "AED",
  "paymentDueDate":            "2026-09-12",
  "contractDocumentReference": "SIL-301358",

  "sellerRegisteredName":     "TRONSTRIDE FZC",
  "sellerVatTrn":             "104196887400003",
  "sellerAddressLine1":       "Business Centre, Sharjah Publishing City Freezone",
  "sellerCity":               "Sharjah",
  "sellerCountrySubdivision": "SHJ",
  "sellerCountryCode":        "AE",
  "sellerLegalRegistrationId":        "714/2015",
  "sellerLegalRegistrationType":      "TL",
  "sellerLegalRegistrationAuthority": "Dubai Economic Department",

  "buyerRegisteredName":     "Habib Mohammad Sharif Abdulla Almulla",
  "buyerVatTrn":             "100711669800003",
  "buyerAddressLine1":       "Dubai, UAE",
  "buyerCity":               "Dubai",
  "buyerCountrySubdivision": "DXB",
  "buyerCountryCode":        "AE",

  "lineExtensionTotal": 37500.00,
  "docLevelDiscount":   0.00,
  "docLevelCharges":    0.00,
  "taxAmount":          1875.00,
  "totalIncludingTax":  39375.00,
  "roundingAmount":     0.00,
  "payableAmount":      39375.00,

  "lines": [
    {
      "lineNumber":        1,
      "itemName":          "Safe Oilgo",
      "itemDescription":   "Safe Oilgo",
      "sellerItemId":      "ILFGICS003",
      "itemCountryOrigin": "AE",
      "quantity":          15.0,
      "quantityUom":       "H87",
      "unitPrice":         2500.00,
      "priceBaseQty":      1.0,
      "lineNetAmount":     37500.00,
      "taxCategory":       "S",
      "taxRatePercent":    5.0,
      "taxScheme":         "VAT",
      "lineTaxAmount":     1875.00,
      "inclVatAmount":     39375.00
    }
  ],

  "allowances": [],

  "payments": [
    {
      "paymentMeansCode":    "30",
      "creditAccountIban":   "AE070260001012029678901",
      "creditAccountScheme": "IBAN"
    }
  ],

  "terms": [
    {
      "termNote":        "60 days from invoice date",
      "termAmount":      39375.00,
      "termCurrency":    "AED",
      "termInstallDate": "2026-09-12"
    }
  ]
}
2.2GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/examples

Worked examples

Returns the ten worked scenarios as ready-to-post bodies — the same cases the Easy Invoice Examples panel offers.

Notes

The bodies come without companyId, seller fields or dates — fill those in and POST to createFull.
The Scenarios section below explains what each key demonstrates.
json
{
  "examples": [
    {
      "key":        "standard-aed",
      "title":      "Standard VAT invoice to a UAE customer",
      "when":       "You are VAT registered and selling inside the UAE.",
      "summary":    "Type 380, transaction type 00000000, AED, 5% standard-rated lines.",
      "highlights": ["Buyer TRN required", "Emirate codes on both addresses"],
      "body":       { "...": "a complete createFull body" }
    }
  ]
}

4Manage entries

Read, list, submit, update, delete and inspect the validation result of an entry.

4.1GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/{entryId}

Get e-invoice entry by ID

Reads an entry back: every field you sent, plus status, taxStatus, timestamps, attached files and — for XML submissions — the original XML.

Parameters

entryIdreq

The entry id returned by createFull.

Notes

This is the read-back to check after a create — unknown keys were dropped silently, so confirm what actually landed.
submittedAsXml is true when the document was posted as raw UBL and is being sent verbatim.
json
{
  "id":                        45426,
  "documentIdEN":              "IIL-1001309",
  "invoiceTypeCode":           "380",
  "documentCurrencyCode":      "AED",
  "issueDateEN":               "2026-07-14T00:00:00",
  "paymentDueDate":            "2026-09-12T00:00:00",
  "status":                    "ACKNOWLEDGED",
  "taxStatus":                 "ACKNOWLEDGED",
  "supplyPartyNameEN":         "TRONSTRIDE FZC",
  "supplyVatIdEN":             "104196887400003",
  "customerPartyNameEN":       "Habib Mohammad Sharif Abdulla Almulla",
  "customerVatIdEN":           "100711669800003",
  "taxTotalTaxAmount":         1875.00,
  "legalTaxInclusiveAmountEN": 39375.00,
  "legalFinalPayableEN":       39375.00,
  "submittedAsXml":            false,
  "files":                     [],
  "createdAt":                 "2026-07-14T10:30:00",
  "updatedAt":                 "2026-07-14T10:41:12"
}
4.2GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry

List e-invoice entries

Paginated list of entries for one company over an issue-date range.

Parameters

companyIdreq

Filter by company id.

startDatereq

Start of the issue-date range (yyyy-MM-dd).

endDatereq

End of the issue-date range (yyyy-MM-dd).

pageopt

Zero-based page number. Default 0.

perPageopt

Page size. Default 10.

sortByopt

Field to sort by. Default createdAt.

orderByopt

asc or desc. Default desc.

statusopt

Filter by entry status, e.g. DRAFT, SUBMITTED, VALIDATION_PASSED, VALIDATION_FAILED, DELIVERED, ACKNOWLEDGED, REJECTED.

typeopt

OUTBOUND or INBOUND.

searchStringopt

Free-text search across document id and party names.

json
{
  "content": [
    {
      "id":                  45426,
      "documentIdEN":        "IIL-1001309",
      "status":              "ACKNOWLEDGED",
      "issueDateEN":         "2026-07-14T00:00:00",
      "supplyPartyNameEN":   "TRONSTRIDE FZC",
      "customerPartyNameEN": "Habib Mohammad Sharif Abdulla Almulla",
      "legalFinalPayableEN": 39375.00
    }
  ],
  "totalElements": 1,
  "totalPages":    1,
  "pageNumber":    0,
  "pageSize":      10
}
4.3GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/{entryId}/statusTimeline

Status timeline

Every status change on the entry with its timestamp and who caused it.

Parameters

entryIdreq

The entry id.

typereq

OUTBOUND or INBOUND.

json
{
  "statusTimeline": {
    "entryId":    45426,
    "documentId": "IIL-1001309",
    "type":       "OUTBOUND",
    "timeline": [
      { "status": "DRAFT",             "timestamp": "2026-07-14T10:30:00", "updatedBy": "user@example.com" },
      { "status": "SUBMITTED",         "timestamp": "2026-07-14T10:35:00", "updatedBy": "user@example.com" },
      { "status": "VALIDATION_PASSED", "timestamp": "2026-07-14T10:35:12", "updatedBy": "system" },
      { "status": "SENDING",           "timestamp": "2026-07-14T10:35:20", "updatedBy": "system" },
      { "status": "DELIVERED",         "timestamp": "2026-07-14T10:36:02", "updatedBy": "system" },
      { "status": "ACKNOWLEDGED",      "timestamp": "2026-07-14T10:41:12", "updatedBy": "system" }
    ]
  }
}
4.4PUThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/{entryId}

Update or submit an entry

Same body shape as createFull, plus id. Submits the entry, edits an editable one, or updates user fields on a locked one.

Content-Type: application/json

Parameters

entryIdreq

The entry id.

Notes

On DRAFT, VALIDATION_FAILED, REJECTED and TRANSMISSION_FAILED every field can change.
On any other status only userField01 … userField10 are applied. Everything else — including the status and the tax status — is left as it is, and the call still returns success: true with a note saying only user fields were updated.
Only DRAFT and SUBMITTED are accepted as a target status. Sending back the entry's current status is a no-op.
text
// Submit a draft — this is what the Submit button does
{
  "id": 45426,
  "status": "SUBMITTED"
}

// Tag a delivered invoice with an ERP reference (locked entry)
{
  "id": 45426,
  "userField01": "PO-REF-2026-0917"
}
4.5DELETEhttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry

Delete e-invoice entries

Deletes one or more entries by id. Only entries in an editable status can be deleted.

Content-Type: application/json

json
{ "ids": [45426, 45427] }
4.6GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/{entryId}/validationErrors

Get validation errors for an entry

The stored XSD and Schematron result. Run it right after creating to confirm the document passed — an empty list means it reached VALIDATION_PASSED.

Parameters

entryIdreq

The entry id.

Notes

validationStatus is VALID, SCHEMATRON_FAILED or XSD_FAILED.
Every rule id maps to a fix in the Validation rules section below.
json
{
  "entryId":          45426,
  "documentId":       "IIL-1001309",
  "validationStatus": "SCHEMATRON_FAILED",
  "schematronValidation": {
    "failedRules": [
      {
        "id":       "ibr-167-ae",
        "severity": "fatal",
        "location": "/Invoice/cac:InvoiceLine[1]",
        "rule":     "vatExemptReasonCode is mandatory when taxCategory is 'E'."
      }
    ]
  }
}

5Documents

Download the PDF rendition or the exact UBL XML that was sent.

5.1GEThttps://app.aigentrix.ai/external/api/v1/print/xml/{entryId}

Download PDF rendition

Generates and streams the e-invoice as a PDF. Save the response body as a .pdf file.

Parameters

entryIdreq

The entry id.

fileNamereq

Which document to render: outbound_sent, outbound_ack, outbound_report_fta, outbound_confirm_fta, inbound_receive, inbound_ack, inbound_report_fta, inbound_confirm_fta.

text
// Binary PDF stream
Content-Type: application/pdf
Content-Disposition: attachment; filename="outbound_sent.pdf"
5.2GEThttps://app.aigentrix.ai/external/api/v1/print/orgxml/{entryId}

Download raw UBL XML

Downloads the pretty-printed UBL XML stored for the entry — the exact document that went on the network. Use it to forward the signed XML to your buyer or authority.

Parameters

entryIdreq

The entry id.

fileNamereq

Same values as the PDF endpoint above.

xml
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
         xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
         xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:CustomizationID>urn:cen.eu:en16931:2017#conformant#urn:fdc:peppol.eu:2017:poacc:billing:international:ubl:3.0</cbc:CustomizationID>
  <cbc:ID>IIL-1001309</cbc:ID>
  <cbc:IssueDate>2026-07-14</cbc:IssueDate>
  ...
</Invoice>

6Validate without saving

Run the full XSD and Schematron validation without writing anything to the database.

6.1POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/validate

Validate invoices — JSON

Validates one or more invoices against the full XSD and Schematron ruleset without creating any database records. The body is a JSON array of createFull objects.

Content-Type: application/json

Parameters

includeXmlopt

true returns the generated UBL XML for each result. Default false.

Notes

This is the endpoint to wire into your test suite — same rules as a real submission, no side effects and no document count consumed.
json
[
  {
    "companyId": 20,
    "documentId": "TEST-0001",
    "issueDate": "2026-07-14",
    "invoiceTypeCode": "380",
    "invoiceTransactionType": "00000000",
    "documentCurrencyCode": "AED",
    "...": "the rest of a createFull body"
  }
]
6.2POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/upload/validate

Validate Excel — 4-sheet

Parses the 4-sheet bulk-upload workbook and validates every invoice in it without saving anything.

Content-Type: multipart/form-data

Parameters

includeXmlopt

true returns the generated UBL XML per result.

Notes

Only the file field is required — no companyId is needed.
text
// multipart/form-data
file: <sample_einvoice_entry.xlsx>
6.3POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/upload/simple/validate

Validate Excel — 1-sheet

Same as above for the simplified single-sheet workbook.

Content-Type: multipart/form-data

Parameters

includeXmlopt

true returns the generated UBL XML per result.

text
// multipart/form-data
file: <simple_einvoice_entry.xlsx>

7Excel upload

Bulk-create invoices from the Excel template, and get a CSV of what failed.

7.1POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/upload

Bulk upload (Excel)

Bulk-creates invoices from the Excel template. Download the template first, then post the filled workbook.

Content-Type: multipart/form-data

Notes

Existing DRAFT, VALIDATION_FAILED and REJECTED entries with a matching companyId and documentId are overwritten; anything further along is skipped.
Pass the results array to the error-log endpoint to get a CSV of what failed.
text
// multipart/form-data
file:      <sample_einvoice_entry.xlsx>
companyId: 20
7.2GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/upload/template

Download upload template

Downloads the Excel template. Save the response body as an .xlsx file.

text
// Binary XLSX stream
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="sample_einvoice_entry.xlsx"
7.3POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/upload/errorLog

Download error log (CSV)

Post the results array from an upload response and receive a CSV error log.

Content-Type: application/json

json
[
  { "invoiceRef": "INV-001", "status": "SUCCESS", "entryId": 1042, "linesCreated": 2, "allowancesCreated": 1, "paymentsCreated": 1 },
  { "invoiceRef": "INV-003", "status": "SKIPPED", "error": "Entry already exists with status SUBMITTED" },
  { "invoiceRef": "INV-004", "status": "FAILED",  "error": "[ibr-167-ae] Line 1: vatExemptReasonCode is mandatory when taxCategory is 'E'." }
]

8Peppol: own XML and lookup

Send UBL you generated yourself, and check whether a trading partner is reachable on the network.

8.1POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/peppol/create-from-xml

Create from raw UBL XML

Send UBL you generated yourself. The body is the raw XML document; it is validated exactly as received and sent verbatim.

Content-Type: application/xml

Parameters

companyIdreq

Your company id.

environmentopt

SANDBOX (default) or LIVE.

saveOnValidationFailureopt

false (default) means a rejected document is not stored at all; true persists it with its errors.

submitopt

true queues the document for sending immediately.

Notes

userId and organisationId are injected server-side from your API key — do not send them.
The submitted document is kept verbatim and sent exactly as received inside a fresh transport envelope. Nothing is defaulted, reordered or reformatted.
The entry then returns originalXml and submittedAsXml: true. Editing the entry afterwards makes the stored fields the source of truth and submittedAsXml turns false.
The envelope identifiers (sender, receiver, instance id) are ours; a cbc:UUID inside your document is left untouched.
xml
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
         xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
         xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:CustomizationID>urn:cen.eu:en16931:2017#conformant#urn:fdc:peppol.eu:2017:poacc:billing:international:ubl:3.0</cbc:CustomizationID>
  <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
  <cbc:ID>XML-2026-0001</cbc:ID>
  <cbc:IssueDate>2026-07-14</cbc:IssueDate>
  <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
  ...
</Invoice>
8.2POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/peppol/create-from-xml-file

Create from XML file

The same as above with a multipart file upload instead of a raw body.

Content-Type: multipart/form-data

Parameters

companyIdreq

Your company id.

environmentopt

SANDBOX (default) or LIVE.

saveOnValidationFailureopt

Default false.

submitopt

true queues the document for sending immediately.

text
// multipart/form-data
file: <invoice.xml>
8.3POSThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/peppol/lookup/participants

Look up participants (POST)

Check whether trading partners are registered on the Peppol network. Returns the SMP host, the document types they accept and their business card.

Content-Type: application/json

Parameters

environmentopt

SANDBOX (default) or LIVE.

Notes

Maximum 50 identifiers per request.
smlEnvironment is TEST, PRODUCTION or AUTO (both). AUTO is the default.
businessCard comes from the Peppol Directory or, failing that, from the SMP itself — businessCardSource says which.
json
{
  "participantIds": [
    "0235:1041968874",
    "0235:1007116698",
    "0235:9900000097"
  ],
  "smlEnvironment": "AUTO"
}
8.4GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/peppol/lookup/participants

Look up participants (GET)

The same lookup for GET-style clients, with the ids as a comma-separated query parameter.

Parameters

idsreq

Comma-separated participant ids, e.g. 0235:1041968874,0235:1007116698. Maximum 50.

environmentopt

SANDBOX (default) or LIVE.

smlEnvironmentopt

TEST, PRODUCTION or AUTO. Default AUTO.

json
{
  "lookedUpAt":      "2026-07-16T12:34:56Z",
  "requestedCount":  2,
  "registeredCount": 1,
  "results": [
    { "participantId": "0235:1041968874", "registered": true },
    { "participantId": "0235:9900000097", "registered": false, "smpError": "SMP service group not found" }
  ]
}
8.5GEThttps://app.aigentrix.ai/external/api/v1/eInvoiceEntry/peppol/lookup/participants/{participantId}

Look up a single participant

Single-id lookup. URL-encode the id — it contains a colon.

Parameters

participantIdreq

e.g. 0235:1041968874 (encoded as 0235%3A1041968874).

environmentopt

SANDBOX (default) or LIVE.

smlEnvironmentopt

TEST, PRODUCTION or AUTO. Default AUTO.

json
{
  "lookedUpAt":      "2026-07-16T12:34:56Z",
  "requestedCount":  1,
  "registeredCount": 1,
  "results": [
    {
      "participantId":      "0235:1041968874",
      "registered":         true,
      "smpHost":            "b-3f4a....edelivery.tech.ec.europa.eu",
      "smpServiceGroupUrl": "https://b-3f4a.../iso6523-actorid-upis::0235%3A1041968874",
      "documentTypes":      ["busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"]
    }
  ]
}

9Webhooks

Receive pushed status changes and document-ready notifications instead of polling.

9.1GEThttps://app.aigentrix.ai/external/api/v1/webhookSubscriptions/events

Get event catalogue

The event keys your subscription can opt into.

json
{
  "events": [
    { "key": "invoice.status_updated", "description": "Fires when either status or taxStatus changes (one delivery per field), with changedField, previousValue and newValue." },
    { "key": "invoice.document_ready", "description": "Fires when the final PDF and XML documents for an outbound invoice are available." },
    { "key": "invoice.received",       "description": "Fires when an inbound Peppol invoice is received and acknowledged for one of your companies." }
  ]
}
9.2POSThttps://app.aigentrix.ai/external/api/v1/webhookSubscriptions

Create subscription

Registers your endpoint. The response contains the signing secret once and only once.

Content-Type: application/json

Notes

Save the secret immediately — it is never shown again. Rotating issues a new one.
eventTypes takes keys from the catalogue, or ["*"] for all of them.
The subscription starts in PENDING_VERIFICATION and only goes ACTIVE after the handshake below.
json
{
  "companyId":   20,
  "environment": "SANDBOX",
  "url":         "https://your-system.example.com/webhook",
  "eventTypes":  ["*"],
  "description": "Primary integration endpoint"
}
9.3POSThttps://app.aigentrix.ai/external/api/v1/webhookSubscriptions/{id}/verify

Verify subscription

Sends a webhook.verification POST with a random challenge to your URL. Answer it correctly and the subscription flips to ACTIVE.

Parameters

idreq

The subscription id.

Notes

Your endpoint must respond 2xx with { "challenge": "<echoed>", "signature": hex(hmacSHA256(secret, challenge)) }.
Echoing the challenge alone is not enough — a missing or wrong signature keeps the subscription in PENDING_VERIFICATION.
json
{
  "success": true,
  "status":  "ACTIVE"
}
9.4POSThttps://app.aigentrix.ai/external/api/v1/webhookSubscriptions/{id}/test

Send test event

Fires a synchronous webhook.test delivery to an ACTIVE subscription — the fastest way to confirm your endpoint is reachable and your signature check passes.

Parameters

idreq

The subscription id.

json
{
  "success":      true,
  "delivered":    true,
  "responseCode": 200
}
9.5GEThttps://app.aigentrix.ai/external/api/v1/webhookSubscriptions

List subscriptions

Lists the SANDBOX and LIVE subscriptions for a company.

Parameters

companyIdreq

The company id.

json
{
  "subscriptions": [
    {
      "id":          12,
      "companyId":   20,
      "environment": "SANDBOX",
      "url":         "https://your-system.example.com/webhook",
      "eventTypes":  "*",
      "status":      "ACTIVE"
    }
  ]
}
9.6DELETEhttps://app.aigentrix.ai/external/api/v1/webhookSubscriptions/{id}

Delete subscription

Permanently deletes a subscription and its delivery history. Deliveries stop immediately.

Parameters

idreq

The subscription id.

json
{ "success": true }

Field reference

Every key accepted by createFull and by the update call, with its PINT business term, whether the rules require it, and the rule id that fires when it is wrong.

Control fields

Not part of the invoice itself — they tell Aigentrix what to do with it.

JSON fieldTermRequiredNotes
companyIdYesYour company, the seller. Its master record supplies the defaults for the whole seller block.
idUpdate onlyThe entry id, required on PUT.
statusUpdate onlyDRAFT or SUBMITTED. Anything else is rejected unless it equals the current status.
invoiceRefNoYour own reference key, max 50 characters, returned as-is.
validationNotrue validates the document before storing it.
userField01 … userField10NoFree text, never sent to Peppol, editable at any status even after delivery.
files[]NoIds of previously uploaded files to embed as supporting documents.
sourceType, quickbooksSynced, businessCentralSynced, odooSynced, zohoSynced, apAutomationNoIntegration flags, stored only.
userIdNoIgnored — the acting user comes from the API key.

Code lists

Taken from the production PINT-AE v1.04 Schematron. A value outside these lists is rejected at validation.

Invoice type codes (IBT-003)

These four are the only codes the ruleset accepts. 383, 386, 388, 389 and 875 are rejected.

CodeMeaningDocumentSeller TRN
380Commercial invoice — claims payment for goods or services suppliedInvoiceRequired
480Invoice out of scope of tax — issuer is outside the scope of tax and collects noneInvoiceMust be empty
381Credit note — credit information to the relevant partyCreditNoteRequired
81Credit note related to goods or services — the counterpart of 480CreditNoteMust be empty

Transaction type (BTAE-02)

Eight positions, each 0 or 1. 00000000 is a standard supply. Positions can be combined where the rules allow.

PositionPatternMeaningWhat it requires
11XXXXXXXFree trade zone supplybeneficiaryId (ibr-007-ae)
2X1XXXXXXDeemed supply (no consideration)No payment means (ibr-191-ae); due date optional; not allowed on 480 or 81
3XX1XXXXXProfit margin schemeStandard-rate category with extra VAT handling (ibr-116-ae)
4XXX1XXXXSummary invoiceInvoice period start and end (ibr-138-ae)
5XXXX1XXXContinuous supply
6XXXXX1XXAgent (disclosed) billingprincipalId, different from the seller TRN (ibr-137-ae, ibr-176-ae)
7XXXXXX1XE-commerce supplyDelivery address (ibr-142-ae)
8XXXXXXX1ExportBuyer TRN optional (ibr-135-ae); delivery address outside the UAE (ibr-152-ae)

Tax categories (IBT-151)

A 380 or 381 must not consist only of E and O lines (ibr-151-ae).

CodeMeaningRateNotes
SStandard rated5The normal case
ZZero rated0Exports and qualifying supplies
EExempt0Requires vatExemptReasonCode or text
OOutside the scope of VAT0The category used on 480 documents
AEReverse charge0Requires the buyer TRN, rcmCode and itemStandardId; line tax must be 0

One tax breakdown is produced per distinct category and rate — 5 and 5.00 are the same breakdown.

Credit note reason codes (BTAE-03)

Federal Decree-Law 8, Article 61(1). Mandatory on type 381.

CodeMeaning
DL8.61.1.ASupply cancelled
DL8.61.1.BNature of the supply changed
DL8.61.1.CConsideration for the supply changed
DL8.61.1.DGoods or services returned
DL8.61.1.EError in the original tax invoice
VDVoluntary disclosure

Legal registration types (BTAE-15 / BTAE-16)

CodeMeaningAuthority field
TLCommercial or trade licenceIssuing authority name, mandatory (ibr-172-ae)
EIDEmirates ID
PASPassportIssuing country as an ISO code (ibr-012-ae)
CDCabinet decision

Reverse-charge goods and services (BTAE-09)

Decree-Law 8, Article 48. Required on every line with tax category AE.

CodeMeaning
DL8.48.3.1Crude or refined oil
DL8.48.3.2Unprocessed or processed natural gas
DL8.48.3.3Any hydrocarbons
DL8.48.8.1Electronic devices — phones, computers, tablets
DL8.48.8.2Pieces and parts of electronic devices

Payment means (IBT-081)

UNCL 4461. Accepted: 1–68, 70, 74–78, 91–98, ZZZ and Z01–Z08.

CodeMeaning
10In cash
20Cheque
30Credit transfer
31Debit transfer
42Payment to bank account
48Bank card
49Direct debit
54Credit card
55Debit card
57Standing agreement
58SEPA credit transfer
59SEPA direct debit
68Online payment service
97Clearing between partners
ZZZMutually defined

Allowance reason codes

UNTDID 5189, used when isCharge is false.

CodeMeaning
95Discount — the usual one
100Special rebate
102Fixed long-term
103Temporary
104Standard
105Yearly turnover
41 / 42Bonus for works ahead of schedule / other bonus
60Manufacturer's consumer discount
64Special agreement
65Production error discount
66New outlet discount
67Sample discount
68End-of-range discount
70Incoterm discount
71Point-of-sale threshold allowance
88Material surcharge or deduction

Full accepted list: 41 42 60 62 63 64 65 66 67 68 70 71 88 95 100 102 103 104 105.

Charge reason codes

UNTDID 7161, used when isCharge is true. The common ones:

CodeMeaning
DLDelivery
FCFreight service
HDHandling
PCPacking
AAAdvertising
AATRush delivery
LALabelling
AEADiversion
ABKMiscellaneous
ADROther services
AEMClerical
FIFinancing

Full accepted list: AA AAA AAC AAD AAE AAF AAH AAI AAS AAT AAV AAY AAZ ABA ABB ABC ABD ABF ABK ABL ABN ABR ABS ABT ABU ACF ACG ACH ACI ACJ ACK ACL ACM ACS ADC ADE ADJ ADK ADL ADM ADN ADO ADP ADQ ADR ADT ADW ADY ADZ AEA AEB AEC AED AEF AEH AEI AEJ AEK AEL AEM AEN AEO AEP AES AET AEU AEV AEW AEX AEY AEZ AJ AU CA CAB CAD CAE CAF CAI CAJ CAK CAL CAM CAN CAO CAP CAQ CAR CAS CAT CAU CAV CAW CAX CAY CAZ CD CG CS CT DAB DAD DAC DAF DAG DAH DAI DAJ DAK DAL DAM DAN DAO DAP DAQ DL EG EP ER FAA FAB FAC FC FH FI GAA HAA HD HH IAA IAB ID IF IR IS KO L1 LA LAA LAB LF MAE MI ML NAA OA PA PAA PC PL PRV RAB RAC RAD RAF RE RF RH.

UAE country subdivisions (IBT-039 / 054 / 079)

Required whenever the country code is AE. Free text is allowed for other countries.

CodeEmirate
AUHAbu Dhabi
DXBDubai
SHJSharjah
AJMAjman
UAQUmm Al Quwain
RAKRas Al Khaimah
FUJFujairah

Identifiers

IdentifierFormatWhere it goes
TRN — VAT registration15 digits, starts with 1, ends with 03sellerVatTrn, buyerVatTrn (ibr-132-ae)
TIN — tax / corporate tax10 digits, starts with 1sellerOtherId, buyerOtherId (ibr-148-ae)
Peppol participant0235: followed by the TINsupplierParticipantId, customerParticipantId
Electronic address scheme0235sellerElectronicAddressSchemeId, buyerElectronicAddressSchemeId
FTA special addresses0235:9900000097 · 0235:9900000098 · 0235:9900000099Deemed supply · buyer not registered · export. Chosen by the server, not by you.

Common units of measure (IBT-130)

UN/ECE Recommendation 20.

CodeMeaning
EAEach
C62One (unit)
H87Piece
HURHour
DAYDay
MONMonth
ANNYear
KGMKilogram
MTRMetre
LTRLitre
MTKSquare metre
MTQCubic metre

Calculations and rounding

Every amount is a 2-decimal number and the totals must reconcile within 1 fils. The server rebuilds the line total and the tax breakdown from the lines — the values you send for the other totals must match.

RuleFormula
ibr-co-10lineExtensionTotal = Σ lineNetAmount
ibr-co-11 / 12docLevelDiscount = Σ document allowances; docLevelCharges = Σ document charges
ibr-co-13Tax exclusive (IBT-109) = lineExtensionTotal − docLevelDiscount + docLevelCharges
ibr-co-14taxAmount = Σ over breakdowns of round₂(taxable × rate ÷ 100)
ibr-co-15totalIncludingTax = IBT-109 + taxAmount
ibr-co-16payableAmount = totalIncludingTax − prepaidAmount + roundingAmount
ibr-147-aelineNetAmount = quantity × unitPrice ÷ priceBaseQty + line charges − line allowances
ibr-131-aeAllowance amount = baseAmount × percent ÷ 100 when both are given
⚠️

Tax is computed once per breakdown, not per line

This is the single most common reason a document that looks correct in your ERP fails ibr-co-14. With many lines the two figures diverge. On a 76-line invoice the sum of per-line rounded VAT was 2 939.30 while the breakdown VAT on the summed net was 2 939.27 — a 3-fils difference that fails validation.

Send the breakdown figure. If your ERP has to show the other total, put the difference in roundingAmount:

json
"lineExtensionTotal": 58785.44,
"taxAmount":          2939.27,
"totalIncludingTax":  61724.71,
"roundingAmount":     0.03,
"prepaidAmount":      61724.74,
"payableAmount":      0

Line rounding order

Round quantity × price ÷ base to 2 decimals first, then apply the discount or charge, then combine. Rounding at the end instead gives a different answer and fails ibr-147-ae.

Foreign currency

vatInAccountingCurrency = taxAmount × rate and totalInLocalCurrency = totalIncludingTax × rate, both to 2 decimals. The rate itself takes up to 6.

Scenarios

Ten worked cases, each available as a ready body from GET /eInvoiceEntry/examples and as a request in the Postman collection. Only the fields that differ from a standard invoice are listed.

standard-aed

Standard VAT invoice to a UAE customer

You are VAT registered and selling inside the UAE.

  • invoiceTypeCode 380, invoiceTransactionType 00000000, currency AED
  • Buyer TRN present, emirate codes on both addresses
  • Lines with taxCategory S at 5%
foreign-currency-usd

Foreign currency invoice

You invoice in USD, EUR or any non-AED currency.

  • documentCurrencyCode USD and taxCurrencyCode AED
  • exchangeRate, at most 6 decimals
  • vatInAccountingCurrency = taxAmount × rate, totalInLocalCurrency = totalIncludingTax × rate
corporate-tax-only-480

Seller registered for corporate tax only

You have a corporate tax registration but no VAT TRN.

  • invoiceTypeCode 480 (or 81 for the credit note)
  • sellerVatTrn empty, sellerOtherId set to the TIN
  • buyerLegalRegistrationId required; lines restricted to categories E, O or Z
credit-note-381

Credit note

Goods returned, price reduced, or an error in the original invoice.

  • invoiceTypeCode 381 with a creditNoteReasonCode
  • originalInvoiceReference and its date point at the invoice being credited
  • payments must be an empty array — a credit note carries no payment means
discounts-charges-rounding

Discounts, charges and rounding

You give a line discount, a header discount or add a delivery charge.

  • lineDiscountAmount on the line becomes a line-level allowance
  • Header rows in allowances[] feed docLevelDiscount and docLevelCharges
  • Tax is computed once on the breakdown; the difference goes in roundingAmount
deemed-supply

Deemed supply

Gifts, samples or goods taken for own use — no consideration.

  • invoiceTransactionType 01000000
  • payments empty; the due date becomes optional
  • Routed to the FTA deemed-supply address instead of a customer
buyer-not-on-peppol

UAE customer not yet on Peppol

Your customer has a TRN but is not registered on the network.

  • Send the customer's real TRN as normal
  • Leave customerParticipantId empty — the server checks the network and routes automatically
  • The document is still reported to the FTA and delivered to the not-registered address
export

Export

Your customer is abroad.

  • invoiceTransactionType 00000001
  • Foreign buyer without a TRN or TIN; free-text country subdivision
  • Lines zero-rated (Z); delivery address line 1, city and subdivision are mandatory
reverse-charge

Reverse charge

Oil, gas, hydrocarbons or electronic devices to a VAT-registered business.

  • Lines use taxCategory AE with taxRatePercent 0 and lineTaxAmount 0
  • Each line carries an rcmCode and an itemStandardId (GTIN)
  • The buyer TRN is mandatory
free-trade-zone

Free trade zone

Supply inside a designated free zone.

  • invoiceTransactionType 10000000
  • beneficiaryId identifies the party receiving the goods in the zone

Validation rules and how to fix them

GET /eInvoiceEntry/{entryId}/validationErrors returns rule ids. These are the ones you will actually meet.

RuleWhat it meansHow to fix it
ibr-134-aeSeller VAT identifier missingProvide sellerVatTrn, or switch to type 480 / 81 with sellerOtherId.
ibr-132-aeVAT identifier is not a valid TRN15 digits, starts with 1, ends with 03. Check for trailing spaces.
ibr-148-aeTIN format wrong10 digits starting with 1.
ibr-177-aeNeither TRN nor TIN givenProvide one of them.
ibr-150-ae / 181-ae / 172-aeSeller legal registration id, type or authority missingFill the trade licence number, set the type to TL and give the issuing authority — usually in the company master.
ibr-143-ae / 144-aeSeller or buyer address incompleteAddress line 1, city and country subdivision are all required.
ibr-128-aeSubdivision is not an emirate codeUse AUH, DXB, SHJ, AJM, UAQ, RAK or FUJ for AE addresses.
ibr-135-ae / 149-aeBuyer has no identifierGive the buyer TRN, or the TIN, or a legal registration id.
ibr-136-aeBuyer legal registration id missing on 480 / 81Set buyerLegalRegistrationId.
ibr-122-aeTax category not E, O or Z on 480 / 81Change the line categories.
ibr-151-aeA 380 / 381 made up only of E and O linesUse type 480 / 81 instead, or add taxable lines.
ibr-158-ae / 001-aeCredit note reason missing or not in the listSet creditNoteReasonCode from the FTA list.
ibr-191-aePayment means on a credit note or deemed supply, or missing on an invoiceRemove or add the payments array accordingly.
ibr-127-aeDue date missingSet paymentDueDate whenever the amount due is greater than zero.
ibr-141-aeTax point date is not before the issue dateRemove or correct taxPointDate.
ibr-159-ae / 002-ae / 140-ae / 175-ae / 153-aeForeign currency fields wrongexchangeRate (≤ 6 decimals), taxCurrencyCode AED, and both AED restatement totals.
ibr-co-10 … co-16Totals do not reconcileSee the Calculations section — most often tax computed per line instead of per breakdown.
ibr-147-aeLine net ≠ quantity × price ± allowancesRecompute lineNetAmount. Line discounts must actually be sent, not just netted off.
ibr-131-aeAllowance amount ≠ base × percentFix the numbers, or drop baseAmount and percent and send only amount.
aligned-ibrp-s-06Tax category on a line-level allowanceLine-level allowance rows carry no tax category — remove it.
ibr-103-ae / 162-ae / 166-ae / 174-ae / 006-aeReverse charge incompleteBuyer TRN, line tax 0, an rcmCode, and an itemStandardId on every AE line.
ibr-007-ae / 137-ae / 176-aeBeneficiary or principal idbeneficiaryId for a free zone supply; principalId for agent billing, and it must differ from the seller TRN.
ibr-152-ae / 142-aeDelivery address missingLine 1, city and subdivision for exports and e-commerce.
ibr-138-aeSummary invoice without a periodSet invoicePeriodStartDate and invoicePeriodEndDate.
ibr-157-aeDeemed, summary or margin flag on a 480 / 81Change either the transaction type or the invoice type.

Submitting your own UBL XML

If you already generate compliant UBL, send it as-is. Aigentrix validates it, wraps it in a transport envelope and delivers it without touching the document.

Use POST /eInvoiceEntry/peppol/create-from-xml with a raw application/xml body, or the multipart /create-from-xml-file variant.

  • The document is validated against XSD and the PINT-AE Schematron exactly as received.
  • It is then kept verbatim and sent exactly as received inside a fresh transport envelope — nothing is defaulted, reordered or reformatted.
  • It appears in the entry's Files tab and in GET /eInvoiceEntry/{id} as originalXml, with submittedAsXml: true.
  • If the entry is later edited through the API or the screen, the stored fields become the source of truth, the document is regenerated from them, and submittedAsXml turns false.
  • The envelope identifiers — sender, receiver, instance id — are ours. A cbc:UUID inside your document is left untouched.
  • saveOnValidationFailure=false (the default) means a rejected document is not stored at all.

Supporting documents

An invoice can carry informative supporting documents — work reports, certificates, delivery notes, drawings. They travel inside the invoice as base64 with a mime code and a file name.

How it works

  • Every file in the entry's Files tab is embedded in the outgoing document. There is no per-file toggle — remove a file to keep it out.
  • If a file cannot be fetched at send time the dispatch fails and is retried. An invoice is never sent silently without a document the user attached.
  • Received documents keep their original file name and mime code, can be previewed and downloaded, and cannot be deleted from an inbound entry.
  • A copy of the invoice itself as a PDF is explicitly not an attachment.

Allowed mime codes

TypeMime code
PDFapplication/pdf
PNGimage/png
JPEGimage/jpeg
CSVtext/csv
XLSXapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
ODSapplication/vnd.oasis.opendocument.spreadsheet
XMLapplication/xml

Any other file type is refused in the Files tab (rule ibr-cl-24).

Updating, locking and user fields

PUT /eInvoiceEntry/{entryId} takes the same body shape as createFull, plus id. What it is allowed to change depends on where the entry has got to.

Entry statusWhat the update may change
DRAFT, VALIDATION_FAILED, REJECTED, TRANSMISSION_FAILEDEverything. Sending "status": "SUBMITTED" submits the document.
SUBMITTED, PROCESSING, VALIDATION_PASSED, SENDING, DELIVERED, ACKNOWLEDGED, …Only userField01 … userField10. Every other field, the status and the tax status stay as they are; the call returns success: true with the unchanged status and a note saying only user fields were updated.

Tag a delivered invoice with an ERP reference

User fields are free text, are never sent to Peppol, and can be written at any status — which makes them the right place for your own cross-references.

bash
curl -X PUT "https://app.aigentrix.ai/external/api/v1/eInvoiceEntry/45426" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: $AIGENTRIX_API_KEY" \
  -d '{ "id": 45426, "userField01": "PO-REF-2026-0917" }'

Deleting an entry (DELETE /eInvoiceEntry with { "ids": [45426] }) is only possible for the editable statuses above.

Webhooks

Rather than polling for status, register an endpoint and Aigentrix will push each change to you. Three setup calls, then you are done.

Setup

  1. 1POST /webhookSubscriptions with your URL and the events you want. Save the secret from the response — it is shown once.
  2. 2POST /webhookSubscriptions/{id}/verify. Aigentrix sends a challenge; answer it correctly and the subscription goes ACTIVE.
  3. 3POST /webhookSubscriptions/{id}/test to confirm your endpoint is reachable and your signature check passes.

Events

EventWhen it fires
invoice.status_updatedEither status or taxStatus changed — one delivery per field, carrying changedField, previousValue and newValue.
invoice.document_readyThe final PDF and XML documents for an outbound invoice are available.
invoice.receivedAn inbound Peppol invoice was received and acknowledged for one of your companies.
webhook.verificationThe handshake challenge sent by the verify call.
webhook.testA synthetic payload sent by the test call.

Verifying the signature

Every delivery carries these headers. Recompute the HMAC over timestamp + "." + rawBody and compare — never trust an unverified payload.

text
X-Aigentrix-Signature:    t=1785789230,v1=<hex hmacSHA256(secret, t + "." + body)>
X-Aigentrix-Event-Id:     ce484636-ab29-447a-be57-a4d4d8230779
X-Aigentrix-Environment:  SANDBOX

The verification handshake expects a 2xx response with both the echoed challenge and its signature:

javascript
import crypto from "node:crypto";

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-Aigentrix-Signature") ?? "";
  const t  = header.match(/t=([^,]+)/)?.[1];
  const v1 = header.match(/v1=([^,]+)/)?.[1];

  const expected = crypto
    .createHmac("sha256", process.env.AIGENTRIX_WEBHOOK_SECRET)
    .update(t + "." + req.body.toString("utf8"))
    .digest("hex");

  if (!v1 || !crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body.toString("utf8"));

  // The verification handshake must echo the challenge AND sign it.
  if (event.eventType === "webhook.verification") {
    const challenge = event.data.challenge;
    return res.json({
      challenge,
      signature: crypto
        .createHmac("sha256", process.env.AIGENTRIX_WEBHOOK_SECRET)
        .update(challenge)
        .digest("hex"),
    });
  }

  // Handle the real events here.
  res.status(200).end();
});

Example payload

An outbound invoice reaching ACKNOWLEDGED. Failure states — REJECTED, VALIDATION_FAILED, TRANSMISSION_FAILED — carry rejectionReason instead of documents.

json
{
  "eventId":        "ce484636-ab29-447a-be57-a4d4d8230779",
  "eventType":      "invoice.status_updated",
  "environment":    "SANDBOX",
  "occurredAt":     "2026-08-03T20:33:49Z",
  "entryId":        40117,
  "invoiceNumber":  "RTRUC-650020",
  "companyId":      20,
  "organisationId": 27,
  "type":           "OUTBOUND",
  "changedField":   "status",
  "previousValue":  "DELIVERED",
  "newValue":       "ACKNOWLEDGED",
  "documents": [
    { "type": "PDF", "fileName": "outbound_sent", "url": "https://app.aigentrix.ai/external/api/v1/print/xml/40117?fileName=outbound_sent" },
    { "type": "XML", "fileName": "outbound_sent", "url": "https://app.aigentrix.ai/external/api/v1/print/orgxml/40117?fileName=outbound_sent" },
    { "type": "PDF", "fileName": "outbound_ack",  "url": "https://app.aigentrix.ai/external/api/v1/print/xml/40117?fileName=outbound_ack" },
    { "type": "XML", "fileName": "outbound_ack",  "url": "https://app.aigentrix.ai/external/api/v1/print/orgxml/40117?fileName=outbound_ack" }
  ]
}

Errors

A business error arrives with HTTP 200 and success: false — always check the flag, not just the status code.

HTTP status codes

HTTPBodyMeaning
200success: false with errorKey or errorLogA business error — the request was understood but refused.
400errorMalformed request, or an invalid environment value.
401Missing, invalid or revoked API key.
403errorKey: "Access denied"The entry belongs to another organisation.
500errorUnexpected. Contact support with the entryId or documentId.

Common errorKey values

errorKeyMeaning
company.not.foundNo company with that id.
company.not.accessibleThe company exists but belongs to a different organisation.
organisation.not.foundThe API key does not resolve to an organisation.
document.plan.missingThe company has no e-invoicing plan.
document.limit.existThe plan's document limit has been reached.
file.original_xml.protectedThe original XML of an XML-submitted invoice cannot be deleted.
Only DRAFT or SUBMITTED status is allowed.You asked for a status that cannot be set through the API.