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.
npm install unify-sdk@3.0.20-betapnpm add unify-sdk@3.0.20-betayarn add unify-sdk@3.0.20-betabun add unify-sdk@3.0.20-betaConfigure the SDK
Set your key as an environment variable, so it never appears in your code:
export COMPLYANCE_API_KEY='paste-your-sandbox-key'Then configure the SDK once, when your application starts:
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.SANDBOXsends requests to sandbox. UseEnvironment.PRODUCTIONwith a production key when you go live. A key works only in the environment it was created for.Sourcenames 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.
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:
{
"status": "success",
"message": "Unify request processed successfully",
"data": {
"processing": {
"status": "completed"
}
}
}Next, in the Developer portal:
- Create an integration and pick this sample payload.
- Map your fields to GETS until the required fields are covered.
- Link your source (
acme-erp, version1.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.
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.
documentTypecomes from the mapping step.DocType.ofsets the GETS document type: a base, such asBASE.TAX_INVOICEorBASE.CREDIT_NOTE, followed by any modifiers, such asMODIFIER.B2BorMODIFIER.EXPORT. The document type decides which fields are required.Country.AEis 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:
{
"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:
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:
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.
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 bynew SDKConfig(...): the key is empty. Check thatCOMPLYANCE_API_KEYis set in the environment your application runs in.API_ERRORon 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 withEnvironment.SANDBOXand a production key withEnvironment.PRODUCTION. Themessagecarries the reason the request was refused.MISSING_FIELDwithSource name is requiredorSource 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