# Submit and retrieve

Submit a document to Unify, read the response, then follow the document until it is delivered.

## Endpoint

```http title="Submit a document"
POST https://prod.gets.complyance.io/api/v3/unify
```

One request carries one document, or a [batch](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#batch-requests) in an `invoices` array. Complyance reads the `payload` through your source’s mapping, checks the document against the rules for its country and document type, then continues delivery to the tax authority or the Peppol network.

## Headers

| Header | Value | Required |
| - | - | - |
| `Authorization` | `Bearer YOUR_API_KEY`. See [Authentication](https://docs.complyance.io/api-reference/authentication/). | Yes |
| `new-api` | `true`. Selects this request format for a JSON body. Without it, a JSON request is read in the earlier format. | Yes, for JSON |
| `Content-Type` | `application/json`, or `application/xml` for an [XML request](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#xml-requests) | Yes |
| `Accept` | `application/json` | No |

## Request body

The envelope names the country and document type; `payload` carries the invoice data your source sends.

| Field | Type | Description | Required |
| - | - | - | - |
| `country` | string | ISO 3166-1 alpha-2 country code for this document. | Yes |
| `environment` | string | `sandbox` or `production`. Must match your key: a sandbox key for `sandbox`, a production key for `production`. | Yes |
| `purpose` | string | `invoicing`, `validate` or `mapping`. See [Purpose](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#purpose). | Yes |
| `source` | string | Your source, as `name:version`. | Yes, except with `mapping` |
| `documentType` | object | `base`, `modifiers[]` and an optional `variant`. Determines which invoice fields the selected country requires. See [document types](https://docs.complyance.io/api-reference/gets/document-types/). | Yes |
| `payload` | object | The invoice in the shape your source’s mapping reads. With the **Default GETS Mapping**, that shape is GETS itself; see the [GETS field reference](https://docs.complyance.io/api-reference/gets/fields/). | Yes |

A source is the system that sends invoices, such as your ERP, registered in your workspace. Its name and version are the values you gave it when you created it, such as `acme-erp` and `1.0.0`, or `acme-dubai` and `v1` for a company onboarded through Connect. The version is free text, not a version number Complyance checks, but it must match the registered version exactly. Complyance splits `source` at its last colon.

```bash title="Submit a document"
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
```

A complete `invoice.json` for the UAE is in the [quickstart](https://docs.complyance.io/get-started/quickstart/). The [API playground](https://docs.complyance.io/api/?operation=submitDocument) builds a request for any country and sends it to sandbox.

### Formats

Send dates as `YYYY-MM-DD` and amounts as JSON numbers, such as `2100` or `2100.5`. Most countries’ rules allow no more than two decimal places on invoice totals; a value with more fails validation, and the error names the rule.

## Purpose

`purpose` says what Complyance does with the document.

| Value | What happens | Response |
| - | - | - |
| `invoicing` | Validates the document, stores it, produces the e-invoice and continues delivery. | `200` when it passed, `422` when it failed. See [Response](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#response). |
| `validate` | Runs the same checks as `invoicing`, including your mapping and the duplicate-number check, without storing or submitting anything. The document number is not used up. | `200` with `valid` set to `true` or `false`. |
| `mapping` | Saves the payload as a sample in [Payload Manager](https://docs.complyance.io/one-platform/developer-portal/payload-manager/), so you can build a mapping from it. Nothing is validated or submitted. | `200` with a `payloadId`. |

Use `validate` to check a payload before you send it with `invoicing`.

```json title="validate — HTTP 200"
{
  "valid": false,
  "documentNumber": "INV-2026-000184",
  "message": "Your invoice could not be validated. Review the errors and try again.",
  "errors": [
    {
      "code": "IBR-015",
      "getsPath": "totals.amountDue",
      "payloadPath": "totals.amountDue",
      "message": "An Invoice MUST have the Amount due for payment (ibt-115).",
      "severity": "error",
      "ruleSet": "ae:tax_invoice"
    }
  ],
  "warnings": [],
  "validationStage": "gets",
  "limitations": "…"
}
```

When the payload passes, `valid` is `true`, `message` is `Your invoice was validated.` and `errors` is empty. `warnings` lists findings that do not stop the document. `limitations` says in words what `validate` does not do, shortened here.

```json title="mapping — HTTP 200"
{
  "payloadId": "01K5EXAMPLE0000000PAYLOAD01",
  "message": "Payload saved for mapping."
}
```

## Response

For `invoicing`, `200` means the document passed validation and was stored.

```json title="Document validated — HTTP 200"
{
  "documentId": "01K5EXAMPLE00000000000142",
  "documentNumber": "INV-2026-000184",
  "message": "Your invoice was validated and is ready for submission.",
  "Base64XML": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4…"
}
```

| Field | Type | Description |
| - | - | - |
| `documentId` | string | Keep it to look the document up later. |
| `documentNumber` | string | The number from your payload. |
| `message` | string | What happened, in words. |
| `Base64XML` | string | The e-invoice XML, Base64-encoded. Empty for countries where it is not produced at this point. |

`422` means the document failed validation. The body lists every problem in `errors`; see [Errors](https://docs.complyance.io/api-reference/unify/errors/#validation-errors).

## Batch requests

To send several documents in one request, put them in an `invoices` array. Each item is a complete request body with its own `country`, `environment`, `purpose`, `source`, `documentType` and `payload`. Nothing is shared between items, and `purpose` is required on every item.

```json title="Batch request"
{
  "invoices": [
    {
      "country": "AE",
      "environment": "sandbox",
      "purpose": "invoicing",
      "source": "acme-erp:1.0.0",
      "documentType": { "base": "tax_invoice", "modifiers": [] },
      "payload": { "header": { "documentNumber": "INV-2026-000185" } }
    },
    {
      "country": "AE",
      "environment": "sandbox",
      "purpose": "invoicing",
      "source": "acme-erp:1.0.0",
      "documentType": { "base": "tax_invoice", "modifiers": [] },
      "payload": { "header": { "documentNumber": "INV-2026-000186" } }
    }
  ]
}
```

The payloads are shortened here. A batch holds 1 to 10 documents with `invoicing` or `mapping`, or 1 to 100 when every item uses `validate`. A batch that mixes `validate` with the other purposes, or holds more items than its limit, is refused with `400`.

Every item is checked separately, so one failure does not stop the others. The response is HTTP `200` with a summary and one result per item, in the order you sent them:

```json title="Batch response — HTTP 200"
{
  "summary": { "total": 2, "succeeded": 1, "failed": 1 },
  "results": [
    {
      "index": 0,
      "success": true,
      "result": {
        "documentId": "01K5EXAMPLE00000000000143",
        "documentNumber": "INV-2026-000185",
        "message": "Your invoice was validated and is ready for submission.",
        "Base64XML": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4…"
      }
    },
    {
      "index": 1,
      "success": false,
      "result": {
        "documentId": "01K5EXAMPLE00000000000144",
        "documentNumber": "INV-2026-000186",
        "message": "Your invoice could not be validated. Review the errors and try again.",
        "validationStage": "gets",
        "errors": [
          {
            "code": "IBR-015",
            "getsPath": "totals.amountDue",
            "payloadPath": "totals.amountDue",
            "message": "An Invoice MUST have the Amount due for payment (ibt-115).",
            "severity": "error",
            "ruleSet": "ae:tax_invoice"
          }
        ]
      }
    }
  ]
}
```

`index` is the item’s position in `invoices`, counting from 0. When an item was checked, `result` holds the same body a single request returns. When an item could not be processed at all, it has `error` with a `code` and `message` instead, such as `invalid_argument` for a missing `source` or `failed_precondition` for a [mapping that is not ready](https://docs.complyance.io/api-reference/unify/errors/#other-errors). Always read `success` on each item: HTTP `200` only means the batch was read.

If any item’s `environment` does not match your key, the whole batch is refused with `403` and nothing is processed.

## XML requests

If your system produces XML, send the request as XML instead of JSON. Use the same endpoint and `Authorization` header with `Content-Type: application/xml` (optionally with `; charset=utf-8`). The `new-api` header is not needed: an XML body always uses this request format.

The XML has the same fields as the JSON body, as elements under a `UnifyRequest` root. List items go in `item` elements. The `payload` element holds your own invoice XML.

```xml title="XML request"
<?xml version="1.0" encoding="UTF-8"?>
<UnifyRequest>
  <country>AE</country>
  <environment>sandbox</environment>
  <purpose>invoicing</purpose>
  <source>acme-erp:1.0.0</source>
  <documentType>
    <base>tax_invoice</base>
    <modifiers/>
  </documentType>
  <payload>
    <Invoice>
      <InvoiceNumber>INV-2026-000184</InvoiceNumber>
      <IssueDate>2026-09-18</IssueDate>
      <Currency>AED</Currency>
    </Invoice>
  </payload>
</UnifyRequest>
```

For a batch, put each request in an `item` element inside `<invoices>`, and nothing else under the root. The batch limits are the same as for JSON.

- Your mapping reads the `payload` element, so build it from an XML sample: send one with `purpose` set to `mapping`, then map it in the Integration engine.
- Values in `payload` are read as text. Your mapping decides how each one becomes a GETS field.
- The envelope elements take no attributes. Elements inside `payload` can have attributes.
- The body must be UTF-8 and at most 8 MiB (8,388,608 bytes). A larger body is refused with `413`. A different content type, or a compressed body, is refused with `415`.
- DTDs and custom entities are not accepted. Use only the five standard XML entities, such as `&`, or CDATA sections.

The responses are the same as for JSON. When an XML request is refused before it is checked, the body has a `code` such as `invalid_argument` and the message `Something went wrong. Please contact support.` If you cannot find the cause, email <support@complyance.io> with the time of the request.

## Limits

| Limit | Value |
| - | - |
| Documents per batch | 1 to 10, or 1 to 100 when every item uses `validate` |
| XML body | 8 MiB |
| Request rate | Too many requests in a short time return `429`. Wait for the number of seconds in the `Retry-After` header, then send again. |

## Retries and duplicates

Complyance refuses a second document with the same `documentNumber` from your workspace in the same country and environment. That protects you when a request is repeated.

- **The request timed out or the connection dropped.** Do not assume the document was not stored. Send the same request again. If the first one was stored, you get `422` with `GETS-HEAD-010`, and the message names the status the document has, such as `COMPLIANT`. That `422` has no `documentId`: find the document in the Invoice Portal by [searching for its number](https://docs.complyance.io/one-platform/invoice-portal/documents/search-and-filters/#search-by-document-number).
- **The document failed validation.** A `422` with validation errors stores the document as **Needs Correction**. Fix the data and send it again with the same `documentNumber` from the same source. Complyance updates the stored document and keeps its `documentId`.
- **You meant to send a new document.** Give it a number you have not used before.
- **The document was already sent on.** Once a document has passed validation, its number is used, whatever happens next. If the tax authority or the network rejected it, send the corrected document with a new number.

The rule is the same in every country. A document marked **Needs Correction** can only be corrected through the source and integration that sent it; the same number from another source is refused with `GETS-HEAD-010`.

A `documentId` field in the request does not make a retry safe; Complyance assigns its own. Use `validate` to check a document without using up its number.

## Follow the document

Delivery to the tax authority or the Peppol network continues after the response. Follow it with the `documentId`:

```bash title="Check the status"
curl "https://prod.gets.complyance.io/api/v3/documents/${DOCUMENT_ID}?type=sales" \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Accept: application/json'
```

Read `data.state` with `data.isTerminal`. While `isTerminal` is `false`, wait before checking again, and make each wait longer than the last. Stop when `isTerminal` is `true`, or, in Belgium and Germany, when `state` is `delivery_success` or `delivery_failed`. No webhook reports the outcome: `invoice.passed` and `invoice.failed` only say whether the document passed validation when you sent it. Checking the status is how you learn the outcome. Every state and field is in [Documents and status](https://docs.complyance.io/api-reference/unify/documents/).

## Errors

`422` means the document failed validation, and the body lists every problem. Any other error status means the request itself was refused. What each one means, and what to do, is in [Errors](https://docs.complyance.io/api-reference/unify/errors/).

Last updated 16 October 2026

Source: https://docs.complyance.io/api-reference/unify/submit-and-retrieve/
