# 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, a UAE company onboarded in sandbox, and a source with a mapping.

> **Before you start**
>
> - A sandbox API key. Create one in **Developer portal** › **API keys**. See [Authentication and environments](https://docs.complyance.io/api-reference/authentication/).
> - A UAE company [onboarded in sandbox](https://docs.complyance.io/countries/ae/onboarding/). Even in sandbox, this takes the company’s real 10-digit tax identification number (TIN) and its authorization for an accredited service provider (ASP), the provider that exchanges its e-invoices. Complyance verifies both, so a made-up company cannot be onboarded.
> - Your source, as `name:version`. A source is the system that sends invoices to Complyance, such as your ERP. You set it up when you onboard the company.
> - A mapping for the source: how its fields correspond to GETS, the Complyance standard invoice format. This page sends the invoice in GETS, which suits the **Default GETS Mapping** that ISV workspaces in the Partner Platform receive for the UAE. Its test cases must pass in [Testbeds](https://docs.complyance.io/one-platform/developer-portal/testbeds/) before it takes invoices; see [Use the default UAE mapping](https://docs.complyance.io/partner-platform/isv/mappings-and-payloads/). In any other workspace, [create a mapping](https://docs.complyance.io/one-platform/developer-portal/integration-engine/create/) for the source first, and send the invoice in the shape that mapping reads.

## Step 1: Set your key

```bash title="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.

## Step 2: Save the invoice

Save this request body as `invoice.json`:

```json title="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": "DXB"
        },
        "taxIds": [
          {
            "type": "VAT",
            "value": "100012345600003"
          }
        ],
        "registrationNumbers": [
          {
            "type": "TL",
            "value": "CN-1234567"
          }
        ],
        "peppolId": "1000123456"
      },
      "buyer": {
        "name": "Gulf Retail FZ-LLC",
        "address": {
          "country": "AE",
          "addressLine1": "48 Market Street",
          "city": "Dubai",
          "stateOrProvince": "DXB"
        },
        "taxIds": [
          {
            "type": "VAT",
            "value": "100098765400003"
          }
        ],
        "registrationNumbers": [
          {
            "type": "TL",
            "value": "CN-7654321"
          }
        ],
        "peppolId": "1000987654"
      }
    },
    "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": "CREDIT_TRANSFER",
          "creditTransferInfo": {
            "payeeFinancialAccountId": "AE070331234567890123456"
          }
        }
      ]
    },
    "totals": {
      "totalAmountExcludingTax": 2000,
      "totalAmountIncludingTax": 2100,
      "amountDue": 2100,
      "totalLineTaxableAmount": 2000,
      "totalTaxAmount": 100
    },
    "extensions": {
      "peppol_seller_peppolIdScheme": "0235",
      "peppol_buyer_peppolIdScheme": "0235",
      "ae_seller_authorityName": "Dubai Department of Economy and Tourism",
      "ae_buyer_authorityName": "Dubai Department of Economy and Tourism"
    }
  }
}
```

Change these values before you send it:

- `source`: your own source. The sample `acme-erp:1.0.0` is not in your workspace.
- `documentNumber`: a number you have not sent before.
- The seller: your onboarded company’s name and VAT registration number (TRN), and its TIN in `peppolId`. `peppolId` is the 10-digit TIN, and `peppol_seller_peppolIdScheme` is `0235`, the scheme for UAE TINs, so together they are the Peppol ID the company was registered with.

`purpose: invoicing` submits the document; `validate` only checks it. See [Purpose](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#purpose). `environment` must match your key, so keep `sandbox` with a sandbox key. `country` and `documentType` decide which `payload` fields are required. Complyance reads the `payload` through your source’s mapping; with the Default GETS Mapping, the payload is GETS. `stateOrProvince` takes the emirate’s code, such as `DXB` for Dubai.

## Step 3: Send it

**cURL**

```bash title="Submit the invoice"
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.json
```

**JavaScript**

```javascript title="Submit the invoice"
import { 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());
```

**Python**

```python title="Submit the invoice"
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](https://docs.complyance.io/api/?operation=submitDocument&try) in the API playground. It builds this request for any country and sends it to sandbox.

## Step 4: Read the response

You should see HTTP `200`:

```json title="Invoice validated — 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.

## Step 5: Check the status

```bash title="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'
```

```json title="Invoice accepted — HTTP 200 (shortened)"
{
  "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": [],
    "compliance": {
      "uuid": "6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b",
      "ftaApprovedStatus": "APPROVED",
      "businessProcessIdentifier": "urn:peppol:bis:billing",
      "specificationIdentifier": "urn:peppol:pint:billing-1@ae-1"
    },
    "validationResults": {
      "status": "Valid",
      "validationSteps": [
        { "name": "Validation Passed", "status": "Valid", "error": null },
        { "name": "Queue Admitted", "status": "Valid", "error": null },
        { "name": "Delivery Processing Started", "status": "Valid", "error": null },
        { "name": "Final Valid", "status": "Valid", "error": null }
      ]
    }
  }
}
```

While `isTerminal` is `false`, the invoice is still being processed, so check again later, waiting a little longer each time. `final_valid` means it was accepted as compliant. In the Invoice Portal, it shows as **Compliant**. Every state, and the fields this example leaves out, are in [Documents and status](https://docs.complyance.io/api-reference/unify/documents/).

## 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`. The message says a mock API key is required for sandbox: a mock key is a sandbox key. Use a sandbox key with `sandbox`.

**`404`:** the `source` is not in your workspace. Check its name and version.

**`412` with `failed_precondition`:** the source has no mapping for this country and document type, or the mapping’s test cases have not passed. The `reason` says which; see [When the mapping is not ready](https://docs.complyance.io/api-reference/unify/errors/#when-the-mapping-is-not-ready).

**`422` with `GETS-HEAD-010`:** a document with this `documentNumber` is already stored and past validation. If this is a new invoice, give it a number you have not used and send it again. If you were retrying a request that timed out, the first one worked; see [Retries and duplicates](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#retries-and-duplicates).

**`422` with `AE-PEPPOL-008`:** the seller’s `peppolId` is not your onboarded company’s TIN. Use the 10-digit TIN the company was onboarded with.

**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 the same `documentNumber`: Complyance updates the invoice it stored.

## Next step

See which fields your own invoices need in the [field reference](https://docs.complyance.io/api-reference/gets/fields/). Each country’s guide then covers its onboarding, rules and examples: [United Arab Emirates](https://docs.complyance.io/countries/ae/), [Saudi Arabia](https://docs.complyance.io/countries/sa/), [Malaysia](https://docs.complyance.io/countries/my/), [Oman](https://docs.complyance.io/countries/om/), [Belgium](https://docs.complyance.io/countries/be/) and [Germany](https://docs.complyance.io/countries/de/).

Last updated 16 October 2026

Source: https://docs.complyance.io/get-started/quickstart/
