Java SDK
Send invoices to Complyance from a Java application: a sample payload for mapping, then real invoices from the same source, their status and your purchase invoices. You need Java 11 or later, Maven or Gradle, and a sandbox API key.
Install
<dependency>
<groupId>io.complyance</groupId>
<artifactId>unify-sdk</artifactId>
<version>3.0.22-beta</version>
</dependency>implementation 'io.complyance:unify-sdk:3.0.22-beta'The package is on Maven Central. 3.0.22-beta is a prerelease, and it is the latest published version.
Configure the SDK
Configure the SDK once, when your application starts.
import io.complyance.sdk.Environment;
import io.complyance.sdk.GETSUnifySDK;
import io.complyance.sdk.SDKConfig;
import java.util.Collections;
public class Complyance {
// The source registered for your workspace
static final String SOURCE_NAME = "acme-erp";
static final String SOURCE_VERSION = "1.0";
static void configure() {
String apiKey = System.getenv("COMPLYANCE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
throw new IllegalStateException("Set COMPLYANCE_API_KEY first.");
}
SDKConfig config = new SDKConfig(
apiKey,
Environment.SANDBOX,
Collections.emptyList());
GETSUnifySDK.configure(config);
}
}Change these parts:
COMPLYANCE_API_KEY: the environment variable that holds your key. Load it from your secret manager. Never write the key in code.Environment.SANDBOX: useEnvironment.PRODUCTIONwith a production key when you go live. The key’s environment must match.SOURCE_NAMEandSOURCE_VERSION: your source’s name and version, exactly as registered. You pass them with every request.
Send a sample payload for mapping
Send one invoice from your system as it is, with the purpose Purpose.MAPPING. Complyance stores the payload so you can map its fields to GETS, the Complyance standard invoice format, in the Integration engine. Nothing is sent to a tax authority.
import io.complyance.sdk.BASE;
import io.complyance.sdk.Country;
import io.complyance.sdk.GETSUnifySDK;
import io.complyance.sdk.GetsDocumentType;
import io.complyance.sdk.Mode;
import io.complyance.sdk.Operation;
import io.complyance.sdk.Purpose;
import io.complyance.sdk.UnifyResponse;
import java.util.List;
import java.util.Map;
// Your own invoice data, in your system's format
Map<String, Object> payload = Map.of(
"invoice_data", Map.of(
"document_number", "INV-2026-0142",
"invoice_date", "2026-09-22",
"currency_code", "AED",
"total_amount", 5250.00),
"seller_info", Map.of(
"seller_name", "Acme Trading LLC",
"vat_number", "100000000000003"),
"buyer_info", Map.of(
"buyer_name", "Globex Distribution LLC"),
"line_items", List.of(Map.of(
"item_name", "Office chair",
"quantity", 10,
"unit_price", 500.00,
"tax_rate", 5.0)));
GetsDocumentType documentType = GetsDocumentType.builder()
.base(BASE.TAX_INVOICE)
.build();
UnifyResponse response = GETSUnifySDK.pushToUnify(
Complyance.SOURCE_NAME,
Complyance.SOURCE_VERSION,
documentType,
Country.AE,
Operation.SINGLE,
Mode.DOCUMENTS,
Purpose.MAPPING,
payload,
true);
System.out.println(response.getMessage());The last argument, true, selects the request format described in Submit and retrieve. Keep it on every call on this page.
You should see:
Payload saved for mapping.Then build the mapping in the portal:
- Create an integration for the same country and document type. At Payload Selection, choose the payload you sent.
- Map your fields to GETS, then run the testbed until it passes.
- Link your source to the integration in Source management.
Submit an invoice
Once your source is linked to a tested integration, send invoices with the purpose Purpose.INVOICING. Use the same source name and version, so Complyance converts the payload with your mapping.
GetsDocumentType documentType = GetsDocumentType.builder()
.base(BASE.TAX_INVOICE)
.build();
UnifyResponse response = GETSUnifySDK.pushToUnify(
Complyance.SOURCE_NAME,
Complyance.SOURCE_VERSION,
documentType,
Country.AE,
Operation.SINGLE,
Mode.DOCUMENTS,
Purpose.INVOICING,
payload,
true);
if (response.getError() != null) {
// The request was refused, for example an unknown source
System.err.println(response.getCode() + ": " + response.getMessage());
} else if (response.getErrors() != null && !response.getErrors().isEmpty()) {
// The invoice failed validation
for (Map<String, Object> error : response.getErrors()) {
System.err.println(error.get("code") + " " + error.get("getsPath") + ": " + error.get("message"));
}
} else if ("queued".equals(response.getStatus())) {
// The service could not be reached; the SDK saved the request to send again later
System.err.println(response.getMessage());
} else {
System.out.println("Document ID: " + response.getDocumentId());
}The document type is a GETS base, such as BASE.TAX_INVOICE or BASE.CREDIT_NOTE, plus any modifiers that apply, such as .modifiers(MODIFIER.EXPORT) or .modifiers(MODIFIER.CONTINUOUS_SUPPLY). It decides which fields the invoice needs. The SDK refuses a base or modifier that the country does not use before it sends anything.
A validated invoice returns a documentId. Keep it to check the status later.
{
"documentId": "01K5EXAMPLE00000000000142",
"documentNumber": "INV-2026-0142",
"message": "Your invoice was validated and is ready for submission.",
"Base64XML": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4…"
}getDocumentId(), getMessage() and getBase64XML() return these values. Base64XML is the e-invoice XML, Base64-encoded; it is empty for countries where it is not produced at this point.
Send your real test cases in sandbox and run them in the testbed before you switch to production.
Check a document’s status
Delivery to the tax authority or the Peppol network continues after the response. Look the document up by its documentId:
Map<String, Object> result = GETSUnifySDK.getSalesInvoice("01K5EXAMPLE00000000000142");
Map<String, Object> document = (Map<String, Object>) result.get("data");
System.out.println("State: " + document.get("state"));
System.out.println("Finished: " + document.get("isTerminal"));
System.out.println("Tax authority: " + document.get("government"));State: final_valid
Finished: true
Tax authority: {status=APPROVED}Check again until isTerminal is true. Every field of the response is in Get a document. To be told instead of asking, use webhooks.
Retrieve purchase invoices
Purchase invoices are the ones your suppliers send you. Your API key decides the workspace and environment they come from.
Get one by its documentId, from a listing or a purchase webhook:
Map<String, Object> result = GETSUnifySDK.getPurchaseInvoice("01K5EXAMPLE00000000000142");
Map<String, Object> invoice = (Map<String, Object>) result.get("data");
System.out.println(invoice.get("documentNumber") + " " + invoice.get("state"));List the invoices received in a date range, one page at a time:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Map<String, String> filters = new HashMap<>();
filters.put("from", "2026-09-01");
filters.put("to", "2026-09-30");
filters.put("limit", "100");
while (true) {
Map<String, Object> page = GETSUnifySDK.listPurchaseInvoices(filters);
Map<String, Object> data = (Map<String, Object>) page.get("data");
for (Object item : (List<?>) data.get("items")) {
Map<String, Object> invoice = (Map<String, Object>) item;
System.out.println(invoice.get("invoiceNumber") + " from " + invoice.get("supplierName"));
}
if (!Boolean.TRUE.equals(data.get("hasMore"))) {
break;
}
filters.put("cursor", (String) data.get("nextCursor"));
}Keep the same range and limit while you page. The fields and the date rules are in Get a purchase invoice and List purchase invoices.
Handle errors
A refused request throws SDKException. Read the reason from getErrorDetail():
import io.complyance.sdk.SDKException;
try {
UnifyResponse response = GETSUnifySDK.pushToUnify(
Complyance.SOURCE_NAME, Complyance.SOURCE_VERSION, documentType, Country.AE,
Operation.SINGLE, Mode.DOCUMENTS, Purpose.INVOICING, payload, true);
} catch (SDKException e) {
if (e.getErrorDetail() != null) {
System.err.println(e.getErrorDetail().getCode() + ": " + e.getMessage());
System.err.println(e.getErrorDetail().getSuggestion());
}
}The errors you are most likely to meet:
AUTHENTICATION_FAILED: the API key is missing, invalid, revoked or expired. Check thatCOMPLYANCE_API_KEYis set in the environment your application runs in, and that the key is still active.AUTHORIZATION_DENIED: the key belongs to a different environment. Use a sandbox key withEnvironment.SANDBOXand a production key withEnvironment.PRODUCTION.INVALID_ARGUMENTwith the messageInvalid GETS V2 documentType: the base or a modifier is not used in that country. Fix the document type and send again.
A failed validation does not throw. The invoice comes back with getErrors(), one entry for each problem, as in Submit an invoice. Each entry has a code, such as IBR-015, the GETS field in getsPath, the field in your payload in payloadPath, and a message. Fix every entry before you send the invoice again. If the code is GETS-HEAD-010, the document number was already used: change it.
The status and purchase invoice methods throw SDKException with the code API_ERROR for any refused request, and the message names the HTTP status. What each status means is in Errors.
Last updated