TypeScript SDK

Send invoices to Complyance from a Node.js application with the TypeScript SDK, from a sample payload for mapping to a submitted invoice in sandbox. You need Node.js 18 or later and a sandbox API key.

Install

The package is unify-sdk on npm. The current version, 3.0.20-beta, is a prerelease, so install it by its exact version.

Install the SDKbash
npm install unify-sdk@3.0.20-beta

Configure the SDK

Set your key as an environment variable, so it never appears in your code:

Set your keybash
export COMPLYANCE_API_KEY='paste-your-sandbox-key'

Then configure the SDK once, when your application starts:

Configure the SDKtypescript
import {
  Environment,
  GETSUnifySDK,
  SDKConfig,
  Source,
  SourceType,
} from "unify-sdk";

const apiKey = process.env.COMPLYANCE_API_KEY;
if (!apiKey) {
  throw new Error("Set COMPLYANCE_API_KEY before you start.");
}

const config = new SDKConfig(apiKey, Environment.SANDBOX, [
  new Source("acme-erp", "1.0", SourceType.FIRST_PARTY),
]);

GETSUnifySDK.configure(config);
  • Environment.SANDBOX sends requests to sandbox. Use Environment.PRODUCTION with a production key when you go live. A key works only in the environment it was created for.
  • Source names the system your invoices come from, such as your ERP, by a name and a version. Use the same name and version in every request from that system.

The examples below run after this configuration, in the same module.

The SDK keeps a background task running after it is configured, so a script does not exit on its own. At the end of a script, call GETSUnifySDK.stopAutoRetry(). A long-running server does not need it.

Send a sample payload for mapping

Before you can submit invoices, Complyance needs to know how your fields match GETS, the Complyance standard invoice format. Send one invoice in your own format with the purpose Purpose.MAPPING. It is stored as a sample payload that you map in the Developer portal; it is not submitted to a tax authority.

Send a sample payloadtypescript
import {
  BASE,
  Country,
  DocType,
  GETSUnifySDK,
  MODIFIER,
  Mode,
  Operation,
  Purpose,
} from "unify-sdk";

const payload = {
  invoice_data: {
    document_number: "INV-2026-0142",
    invoice_date: "2026-09-18",
    currency_code: "AED",
    total_amount: 10500.0,
  },
  seller_info: { seller_name: "Acme Trading LLC", country_code: "AE" },
  buyer_info: { buyer_name: "Gulf Retail LLC", buyer_country: "AE" },
  line_items: [
    { line_id: "1", item_name: "Office chair", quantity: 10, unit_price: 1000.0 },
  ],
};

const documentType = DocType.of(BASE.TAX_INVOICE, MODIFIER.B2B);

const mapping = await GETSUnifySDK.pushToUnifyWithDocumentType(
  "acme-erp",
  "1.0",
  documentType,
  Country.AE,
  Operation.SINGLE,
  Mode.DOCUMENTS,
  Purpose.MAPPING,
  payload,
);

console.log(mapping.status);

Send a payload with every field your system can produce, so you can map all of them. A successful response looks like this, shortened:

Sample payload receivedjson
{
  "status": "success",
  "message": "Unify request processed successfully",
  "data": {
    "processing": {
      "status": "completed"
    }
  }
}

Next, in the Developer portal:

  1. Create an integration and pick this sample payload.
  2. Map your fields to GETS until the required fields are covered.
  3. Link your source (acme-erp, version 1.0) to the integration.

Submit an invoice

Once your source is linked to an integration, send invoices from the same source with the purpose Purpose.INVOICING. Complyance applies your mapping, checks the invoice and continues delivery for its country.

Submit an invoicetypescript
const response = await GETSUnifySDK.pushToUnifyWithDocumentType(
  "acme-erp",
  "1.0",
  documentType,
  Country.AE,
  Operation.SINGLE,
  Mode.DOCUMENTS,
  Purpose.INVOICING,
  payload,
);

const validation = response.data?.validation;
if (validation?.success === false) {
  for (const error of validation.errors ?? []) {
    console.error(error.code, error.message);
  }
} else {
  console.log("Document ID:", response.data?.document?.documentId);
}
  • The source name and version must match the source you linked to the integration.
  • documentType comes from the mapping step. DocType.of sets the GETS document type: a base, such as BASE.TAX_INVOICE or BASE.CREDIT_NOTE, followed by any modifiers, such as MODIFIER.B2B or MODIFIER.EXPORT. The document type decides which fields are required.
  • Country.AE is the country the invoice is issued in.

status is "success" when Complyance received the request, even if the invoice failed its checks. Always read data.validation as well. An invoice that failed shows each problem with its code:

Invoice failed validationjson
{
  "status": "success",
  "data": {
    "document": {
      "documentId": "01K5EXAMPLE00000000000142"
    },
    "validation": {
      "success": false,
      "errors": [
        {
          "code": "GETS-LINE-005",
          "message": "Line item price is required",
          "path": ["lineItems", 0, "price", "amount"]
        }
      ]
    }
  }
}

path points to the GETS field. Fix the field in your payload or in your mapping, then send the invoice again. Keep the documentId to check the document later.

Run your mapping against test cases in a testbed before you switch to production.

Check a document’s status

Delivery to the tax authority or the Peppol network continues after the response. Ask for a document’s current status with its documentId:

Check a document's statustypescript
const status = await GETSUnifySDK.getDocumentStatus("01K5EXAMPLE00000000000142");
console.log(status);

To be told when the status changes instead of asking, use webhooks.

Retrieve purchase invoices

Purchase invoices are the invoices your suppliers send you. Get one by the documentId from a purchase invoice webhook:

Get a purchase invoicetypescript
const invoice = await GETSUnifySDK.getPurchaseInvoice("01JEXAMPLE0000000000000142");
console.log(invoice);

The fields of the result are described in Get a purchase invoice. To list the purchase invoices received in a date range, use List purchase invoices in the API.

Handle errors

When a request is refused, the SDK throws an SDKException. Its code says what went wrong, message describes it and suggestion says what to try.

Handle errorstypescript
import { SDKException } from "unify-sdk";

try {
  const response = await GETSUnifySDK.pushToUnifyWithDocumentType(
    "acme-erp",
    "1.0",
    documentType,
    Country.AE,
    Operation.SINGLE,
    Mode.DOCUMENTS,
    Purpose.INVOICING,
    payload,
  );
  console.log(response.status);
} catch (error) {
  if (error instanceof SDKException) {
    console.error(error.code, error.message, error.suggestion);
  } else {
    throw error;
  }
}

The errors people hit most:

  • API key is required, thrown by new SDKConfig(...): the key is empty. Check that COMPLYANCE_API_KEY is set in the environment your application runs in.
  • API_ERROR on your first request: most often the key was refused because it is invalid, revoked or expired, or belongs to the other environment. Use a sandbox key with Environment.SANDBOX and a production key with Environment.PRODUCTION. The message carries the reason the request was refused.
  • MISSING_FIELD with Source name is required or Source version is required: an invoicing request was sent without a source. Pass the source name and version you linked to your integration.

Validation problems in the invoice itself do not throw; they come back in data.validation, as shown in Submit an invoice. What each error means, and what to do, is in Unify errors.

Next, try a request without writing code in the API playground.

Last updated