Send your first invoice
Submit a UAE tax invoice to sandbox with one API request, then check its status. You need a sandbox API key and the source your invoices come from.
Set your key
export COMPLYANCE_API_KEY='paste-your-sandbox-key'The examples on this page read the key from COMPLYANCE_API_KEY, so it never appears in your code.
Save the invoice
Save this request body as invoice.json:
{
"country": "AE",
"environment": "sandbox",
"purpose": "invoicing",
"source": "acme-erp:1.0.0",
"documentType": {
"base": "tax_invoice",
"modifiers": []
},
"payload": {
"header": {
"documentType": {
"base": "tax_invoice"
},
"documentNumber": "INV-2026-000184",
"issueDate": "2026-09-18",
"currency": "AED",
"dueDate": "2026-10-18"
},
"parties": {
"seller": {
"name": "Acme Trading LLC",
"address": {
"country": "AE",
"city": "Dubai",
"addressLine1": "12 Harbour Road",
"stateOrProvince": "Dubai"
},
"taxIds": [
{
"type": "VAT",
"value": "100012345600003"
}
],
"registrationNumbers": [
{
"type": "CRN",
"value": "CN-1234567"
}
],
"peppolId": "100012345600003"
},
"buyer": {
"name": "Gulf Retail FZ-LLC",
"address": {
"country": "AE",
"addressLine1": "48 Market Street",
"city": "Dubai",
"stateOrProvince": "Dubai"
},
"taxIds": [
{
"type": "VAT",
"value": "100012345600004"
}
],
"peppolId": "100012345600004"
}
},
"lineItems": [
{
"id": "1",
"name": "Cloud subscription — Business plan",
"quantity": 20,
"unitCode": "PCE",
"taxAmount": 100,
"lineTaxableValue": 2000,
"taxCategory": "S",
"taxRate": 5,
"price": {
"amount": 100,
"grossPrice": 100
}
}
],
"payment": {
"paymentMeans": [
{
"paymentMeansCode": "PAYMENT_TO_BANK_ACCOUNT"
}
]
},
"totals": {
"totalAmountExcludingTax": 2000,
"totalAmountIncludingTax": 2100,
"amountDue": 2100,
"totalLineTaxableAmount": 2000,
"totalTaxAmount": 100
},
"extensions": {
"peppol_seller_peppolIdScheme": "0235",
"peppol_buyer_peppolIdScheme": "0235"
}
}
}Change two values before you send it:
source: your own source. The sampleacme-erp:1.0.0is not in your workspace.documentNumber: a number you have not sent before.
environment must match your key, so keep sandbox with a sandbox key. country and documentType decide which payload fields are required. The payload is the invoice itself, in GETS, the Complyance standard invoice format.
Send it
curl --request POST 'https://prod.gets.complyance.io/api/v3/unify' \
--header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'new-api: true' \
--data @invoice.jsonimport { readFile } from 'node:fs/promises';
const response = await fetch('https://prod.gets.complyance.io/api/v3/unify', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.COMPLYANCE_API_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/json',
'new-api': 'true',
},
body: await readFile('invoice.json', 'utf8'),
});
console.log(response.status, await response.json());import os
import requests
with open("invoice.json") as f:
body = f.read()
response = requests.post(
"https://prod.gets.complyance.io/api/v3/unify",
headers={
"Authorization": f"Bearer {os.environ['COMPLYANCE_API_KEY']}",
"Content-Type": "application/json",
"Accept": "application/json",
"new-api": "true",
},
data=body,
)
print(response.status_code, response.json())Send new-api: true with every submission. Without it, the request is read in the earlier format.
To try it in your browser first, open Submit a document in the API playground. It builds this request for any country and sends it to sandbox.
Read the response
You should see HTTP 200:
{
"documentId": "01K5EXAMPLE00000000000142",
"documentNumber": "INV-2026-000184",
"message": "Your invoice was validated and is ready for submission.",
"Base64XML": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4…"
}200 means the invoice passed validation and was stored. Delivery to the tax authority or the Peppol network continues after the response. Base64XML is the e-invoice that was produced, Base64-encoded.
Save the documentId. You need it to check the status.
Check the status
export DOCUMENT_ID='01K5EXAMPLE00000000000142'
curl "https://prod.gets.complyance.io/api/v3/documents/${DOCUMENT_ID}?type=sales" \
--header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
--header 'Accept: application/json'{
"success": true,
"data": {
"documentId": "01K5EXAMPLE00000000000142",
"documentNumber": "INV-2026-000184",
"country": "AE",
"environment": "sandbox",
"state": "final_valid",
"isTerminal": true,
"lastUpdatedAt": "2026-09-22T10:00:04.000Z",
"errors": [],
"government": {
"status": "APPROVED"
},
"validationResults": {
"status": "Valid",
"validationSteps": [
{
"name": "schema",
"status": "Valid",
"error": null
}
]
}
}
}While isTerminal is false, the invoice is still being processed, so check again later. final_valid means it was accepted as compliant. In the Invoice Portal, it shows as Compliant.
If something goes wrong
401 with {"error": "Unauthorized"}: the key is missing, invalid, revoked or expired. Check that COMPLYANCE_API_KEY is set in the shell you send from.
403 with permission_denied: the key belongs to a different environment from environment. Use a sandbox key with sandbox.
404: the source is not in your workspace. Check its name and version.
422 with GETS-HEAD-010: the documentNumber was already sent. Change it and send the invoice again.
Any other 422: the invoice failed validation. Each entry in errors names the field in GETS (getsPath) and in your request (payloadPath), and the rule it broke. Fix the field, then send the invoice again with a new documentNumber.
Next step
See which fields your own invoices need in the field reference. Each country’s guide then covers its onboarding, rules and examples: United Arab Emirates, Saudi Arabia, Malaysia, Oman, Belgium and Germany.
Last updated