OZYN
Developers · API Reference

Integrate your ERP with the UAE Peppol network

One REST endpoint turns your accounting-system invoice into a validated PINT-AE document, sends it over Peppol, and reports it to the FTA. Send us structured JSON — we handle the UBL, validation, receiver routing, fallbacks and reporting. This page lists every endpoint, field and code value you need to integrate on your own.

Authentication

All requests use a per-tenant API token as a Bearer header. Create and rotate tokens in the portal under API Tokens. Your token identifies the sender — the seller on every document is your own Peppol identity, so tokens cannot impersonate another business.

Base URLhttps://ozeinvoice.ae/api/v1
# every request carries your token
Authorization: Bearer pp_your_token_here
Content-Type: application/json

Send an invoice

POST https://ozeinvoice.ae/api/v1/invoices

Post the invoice as JSON. We build the PINT-AE UBL, validate it, derive the receiver, send over Peppol and report to the FTA. You never send the seller — it is taken from your tenant profile.

# complete invoice — all required fields
curl -X POST https://ozeinvoice.ae/api/v1/invoices \
  -H "Authorization: Bearer pp_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "doc_type": "Invoice",
    "invoice_number": "INV-1001",
    "issue_date": "2026-09-14",
    "due_date": "2026-10-14",
    "currency": "AED",
    "payment_means_code": "30",
    "transaction_type": "Standard",
    "buyer": {
      "name": "Acme Trading LLC",
      "trn": "100000000000003",
      "address": "Sheikh Zayed Road",
      "city": "Dubai",
      "state": "Dubai",
      "country": "AE"
    },
    "lines": [
      { "description": "Consulting", "qty": 1, "unit": "HUR",
        "price": 500.00, "vat_rate": 5, "tax_classification": "S" }
    ]
  }'
No TRN? Leave trn empty and we route the buyer to the FTA fallback endpoint automatically. No emirate? If you send city we use it as the emirate when the state is blank.

Request fields

Send raw values — mapping of your own units, tax labels and payment methods to the code lists below is your side; validation and compliance are ours.

Legend. required send it on every document. Where a default is noted we apply it automatically if you omit the field — but a production integration should set it deliberately, not rely on the default. optional is a matching aid only.

FieldReqNotes
invoice_numberrequiredUnique per account. A repeat returns 409 duplicate.
issue_daterequiredYYYY-MM-DD.
due_daterequiredDefault if omitted: your configured payment term, else due on receipt.
currencyrequiredDefault if omitted: AED.
doc_typerequiredInvoice or CreditNote. Default if omitted: Invoice.
payment_means_coderequiredUNCL 4461 code. Default if omitted: 30. table
transaction_typerequiredLabel or 8-flag code. Default if omitted: Standard. table
buyer.namerequiredBuyer legal name.
buyer.trnrequired15-digit TRN. If the buyer has none, leave blank → routed to the FTA fallback endpoint.
buyer.address / city / state / countryrequiredStreet, city, emirate, country. Emirate falls back to city if state is blank. zip optional.
buyer.erp_idoptionalYour customer id — we match/upsert on it.
lines[].descriptionrequiredLine description.
lines[].qtyrequiredDefault if omitted: 1.
lines[].unitrequiredUN/ECE code. Default if omitted: C62. table
lines[].pricerequiredUnit price, exclusive of VAT.
lines[].vat_raterequiredPercentage, e.g. 5 or 0.
lines[].tax_classificationrequiredS/Z/E/O or a label. Default if omitted: inferred from the rate. table
lines[].erp_idoptionalYour item id — we match/upsert the catalogue on it.
The seller is never sent. Your company details, address, TRN and IBAN come from your tenant profile — set them once in the portal.

Field-name aliases (PINT-AE names)

If you prefer the official PINT-AE / MoF field names, you can send those instead of the short names above — both are accepted (the short name wins if you send both):

Short namePINT-AE alias
currencyinvoice_currency_code
transaction_typeinvoice_transaction_type_code
payment_means_codepayment_means_type_code
due_datepayment_due_date
buyer.namebuyer.buyer_name
buyer.trnbuyer.buyer_tax_identifier
buyer.addressbuyer.buyer_address_line_1
buyer.citybuyer.buyer_city
buyer.statebuyer.buyer_country_subdivision
buyer.countrybuyer.buyer_country_code
lines[].qtylines[].invoiced_quantity
lines[].unitlines[].unit_of_measure_code
lines[].pricelines[].item_net_price
lines[].vat_ratelines[].invoiced_item_tax_rate
lines[].tax_classificationlines[].invoiced_item_tax_category_code
lines[].descriptionlines[].item_name / item_description
credit_reason_codecredit_note_reason_code
preceding_invoice_numberpreceding_invoice_reference

Credit notes

A credit note takes all the same fields as an invoice above (buyer, lines, currency, payment means, transaction type — everything except a due date, which a credit note doesn't carry). You simply set doc_type to CreditNote and add the fields below on top:

Additional fieldReqNotes
doc_typeyesCreditNote
credit_reason_codeyesReason code. table (defaults to DL8.61.1.E if omitted).
credit_reason_textnoFree-text explanation (shown as description).
preceding_invoice_numberyes*The original invoice this corrects. *Not required for VD (volume discount).
preceding_invoice_datenoOriginal invoice date.
# complete credit note — same fields as an invoice, plus the credit-note ones
curl -X POST https://ozeinvoice.ae/api/v1/invoices \
  -H "Authorization: Bearer pp_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "doc_type": "CreditNote",
    "invoice_number": "CN-2001",
    "issue_date": "2026-09-20",
    "currency": "AED",
    "transaction_type": "Standard",
    "credit_reason_code": "DL8.61.1.D",
    "credit_reason_text": "Goods returned by customer",
    "preceding_invoice_number": "INV-1001",
    "preceding_invoice_date": "2026-09-14",
    "buyer": {
      "name": "Acme Trading LLC", "trn": "100000000000003",
      "address": "Sheikh Zayed Road", "city": "Dubai", "state": "Dubai", "country": "AE"
    },
    "lines": [
      { "description": "Consulting", "qty": 1, "unit": "HUR",
        "price": 500.00, "vat_rate": 5, "tax_classification": "S" }
    ]
  }'
Volume-discount credit note? Set credit_reason_code to VD — then preceding_invoice_number is not required.

Testing — dry run

Add ?dry_run=1 (or "dry_run": true) to validate a payload without sending anything. Nothing is sent over Peppol, nothing is reported to the FTA, and it does not count toward your monthly usage. Use it while you build.

curl -X POST "https://ozeinvoice.ae/api/v1/invoices?dry_run=1" \
  -H "Authorization: Bearer pp_your_token" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Responses & errors

Success

{ "ok": true, "data": { "document_id": 123, "status": "SENT" } }

Dry run

{ "ok": true, "data": {
  "dry_run": true, "valid": true,
  "errors": [], "error_details": [],
  "schematron_version": "PINT AE 1.0.4"
} }

Validation failure — per-field

{ "ok": true, "data": {
  "valid": false,
  "error_details": [
    { "code": "ibr-144-ae", "field": "buyer.address",
      "message": "The buyer address is incomplete — provide street, city and emirate." }
  ]
} }

Other statuses: 400 missing/invalid field, 401 bad token, 409 duplicate invoice number.

Idempotency

Retries are safe. An invoice number is unique per account, and you can also send an Idempotency-Key header — a repeat with the same key returns the original result instead of sending twice.

Idempotency-Key: INV-1001

Ping — check your connection

GET https://ozeinvoice.ae/api/v1/ping

Returns your tenant identity and capabilities. No side effects — a safe way to confirm your token and base URL are correct.

Receiving invoices

GET https://ozeinvoice.ae/api/v1/received
GET https://ozeinvoice.ae/api/v1/received/get?id={id}

List inbound documents received on Peppol, or fetch one — as raw UBL and as a parsed JSON summary (supplier, buyer, lines, tax, totals). You can also subscribe to invoice.received webhooks in the portal instead of polling.

Payment means (UNCL 4461)

Send the code in payment_means_code. Any valid UNCL 4461 code is accepted; the common ones:

CodeMeaning
10Cash
20Cheque
30Credit transfer (bank transfer) — default; requires a payee IBAN
42Payment to bank account
48Bank card
49Direct debit
54Credit card
55Debit card
58SEPA credit transfer
59SEPA direct debit
97Clearing between partners
1Instrument not defined

Transaction types (BTAE-02)

Send the label (or the 8-flag code) in transaction_type.

LabelCodeMeaning
Standard00000000Ordinary domestic taxable supply (default)
Export00000001Export of goods/services
Free Zone supply10000000Supply within/through a designated free zone
Deemed supply01000000Deemed supply
Margin scheme00100000Profit-margin scheme
Summary invoice00010000Summary (periodic) invoice
Continuous supply00001000Continuous supply
Disclosed agent billing00000100Disclosed agent billing
E-commerce00000010E-commerce supply

Tax classification

Per line, in tax_classification. A label like "Zero-rated" is accepted too; if null we infer it from the VAT rate.

CodeMeaning
SStandard rate (5%)
ZZero-rated (0%)
EExempt
OOut of scope

Credit-note reasons

Send the code in credit_reason_code (a label is accepted).

CodeReason
DL8.61.1.ASupply was cancelled
DL8.61.1.BTax treatment changed (nature of supply changed)
DL8.61.1.CAgreed consideration altered (e.g. bad-debt relief)
DL8.61.1.DGoods/services returned and consideration returned
DL8.61.1.EOther (per code list)
VDVolume discount — may omit the preceding invoice

Units of measure (UN/ECE Rec 20)

Per line, in unit. Blank defaults to C62.

CodeUnit
C62Piece / one (default when blank)
EAEach
HURHour
DAYDay
MONMonth
KGMKilogram
MTRMetre
LTRLitre
MTKSquare metre
MTQCubic metre
TNETonne
SETSet
Need your API token or a live test console? Sign in to the portal — tokens and a dry-run tester live under Integrations and API Tokens.