PHP SDK
Send invoices to Complyance from a PHP application: first a sample payload to build your mapping, then real invoices from the same source. You need PHP 7.4 or later, Composer and an API key.
Install
composer require io.complyance/unify-sdk:3.0.11Version 3.0.11 is the latest, and it is a stable release. It needs PHP 7.4 or later. Run the command from your project directory, then load Composer’s autoloader in your code.
Configure the SDK
Configure the SDK once, when your application starts.
<?php
require __DIR__ . '/vendor/autoload.php';
use ComplyanceSDK\GETSUnifySDK;
use ComplyanceSDK\Models\SDKConfig;
use ComplyanceSDK\Enums\Environment;
$config = new SDKConfig(
getenv('COMPLYANCE_API_KEY'),
Environment::from(Environment::SANDBOX)
);
GETSUnifySDK::configure($config);
// The source this application sends as, and its version.
$sourceName = 'acme-erp';
$sourceVersion = '1.0';- The environment must match the key. Use
Environment::SANDBOXwith a sandbox key. When you go live, useEnvironment::PRODUCTIONwith a production key. $sourceNameand$sourceVersionname the source: the system that sends invoices to Complyance. Send the same pair in every call. When you go live, the source must be registered in your workspace. See Manage sources and their links.- Never write the key into your code. If
COMPLYANCE_API_KEYis not set, the SDK cannot send anything.
Retries
The SDK tries a request up to five times when the network fails or the API answers 429, 500, 502, 503 or 504. To change this, pass a retry configuration as the fourth argument of SDKConfig:
use ComplyanceSDK\Models\RetryConfig;
$config = new SDKConfig(
getenv('COMPLYANCE_API_KEY'),
Environment::from(Environment::SANDBOX),
[],
RetryConfig::conservativeConfig()
);The presets are RetryConfig::defaultConfig(), RetryConfig::conservativeConfig() and RetryConfig::aggressiveConfig(). For any other number of attempts, use new RetryConfig(3); new RetryConfig(1) sends each request once.
Send a sample payload for mapping
Your system has its own names for invoice fields. Before Complyance can turn your invoices into e-invoices, you build a mapping from your fields to GETS, the Complyance standard invoice format. To start, send one invoice as your system produces it, with the purpose MAPPING. Complyance stores it for mapping and does not validate it or send it anywhere. The purposes are described in Submit and retrieve.
use ComplyanceSDK\Enums\Country;
use ComplyanceSDK\Enums\Mode;
use ComplyanceSDK\Enums\Operation;
use ComplyanceSDK\Enums\Purpose;
use ComplyanceSDK\Models\GetsDocumentType;
// Your invoice, in your system's own field names.
$payload = [
'invoice_number' => 'INV-2026-0142',
'issue_date' => '2026-09-30',
'currency' => 'AED',
'seller' => ['name' => 'Acme Trading LLC', 'trn' => '100000000000003'],
'buyer' => ['name' => 'Gulf Office Supplies LLC', 'trn' => '100000000000017'],
'lines' => [
['description' => 'Office chair', 'quantity' => 10, 'unit_price' => 500.00, 'vat_rate' => 5],
],
'total' => 5250.00,
];
$documentType = GetsDocumentType::builder()
->base('tax_invoice')
->build();
$raw = GETSUnifySDK::pushToUnifyWithDocumentType(
$sourceName,
$sourceVersion,
$documentType,
Country::from(Country::AE),
Operation::from(Operation::SINGLE),
Mode::from(Mode::DOCUMENTS),
Purpose::from(Purpose::MAPPING),
$payload,
null,
true
);
$response = json_decode($raw, true);
echo $response['message'];The last argument, true, sends the new-api: true header, so the request and the response use the current Unify format described in Submit and retrieve. Pass it in every call on this page. Without it, the SDK sends the earlier format.
You should see:
{
"message": "Payload saved for mapping."
}Next, open the Integration engine in the Developer portal and create an integration. At Payload Selection, choose the payload you sent. Map its fields, test the result, and link the integration to your source. See Create an integration and Map your fields to GETS.
Submit an invoice
Once the integration is tested and linked to your source, send invoices with the purpose INVOICING. Use the same source name and version, and the same payload shape you mapped. Complyance converts the invoice with your mapping, validates it, and continues delivery to the tax authority or the Peppol network.
$documentType = GetsDocumentType::builder()
->base('tax_invoice')
->build();
$raw = GETSUnifySDK::pushToUnifyWithDocumentType(
$sourceName,
$sourceVersion,
$documentType,
Country::from(Country::AE),
Operation::from(Operation::SINGLE),
Mode::from(Mode::DOCUMENTS),
Purpose::from(Purpose::INVOICING),
$payload,
null,
true
);
$response = json_decode($raw, true);
$documentId = $response['documentId'];
echo $response['message'];The document type is the GETS document type: a base such as tax_invoice or credit_note, plus modifiers that describe the transaction. For an export invoice, add ->modifiers(['export']) before ->build(). Modifiers can change which fields are required. The fields each combination needs are in the field reference.
You should see:
{
"documentId": "01K5EXAMPLE00000000000142",
"documentNumber": "INV-2026-0142",
"message": "Your invoice was validated and is ready for submission.",
"Base64XML": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4…"
}The invoice passed validation and was stored. Save the documentId; you need it to check the status. Base64XML is the e-invoice that was produced, Base64-encoded. Every field is described in Submit and retrieve.
In sandbox and production the SDK sends invoices for Country::SA, Country::MY, Country::AE, Country::BE and Country::DE. It has no country value for Oman.
Run your invoices in sandbox, and through the testbed, until they pass. Then switch to a production key and Environment::PRODUCTION.
Check a document’s status
Delivery continues after the response. Follow it with the documentId:
$status = GETSUnifySDK::getDocumentStatus($documentId);
echo $status['data']['state'];{
"success": true,
"data": {
"documentId": "01K5EXAMPLE00000000000142",
"documentNumber": "INV-2026-0142",
"country": "AE",
"environment": "sandbox",
"state": "final_valid",
"isTerminal": true,
"errors": []
}
}While isTerminal is false, the invoice is still being processed, so check again later. final_valid means it was accepted as compliant. final_invalid or rejected means it was not; errors says why. Checking the status is how you learn whether the invoice was accepted. Every state and field is described in the documents reference.
Retrieve purchase invoices
To read invoices your suppliers sent you, call the API directly with the same key: Get a purchase invoice by its documentId, or List purchase invoices for a date range, one page at a time.
Handle errors
When Complyance refuses a request, the call throws ComplyanceSDK\Exceptions\SDKException. Read the HTTP status and the response body from its error detail:
use ComplyanceSDK\Exceptions\SDKException;
try {
$raw = GETSUnifySDK::pushToUnifyWithDocumentType(
$sourceName,
$sourceVersion,
$documentType,
Country::from(Country::AE),
Operation::from(Operation::SINGLE),
Mode::from(Mode::DOCUMENTS),
Purpose::from(Purpose::INVOICING),
$payload,
null,
true
);
} catch (SDKException $e) {
$detail = $e->getErrorDetail();
$httpStatus = $detail->getContextValue('httpStatus');
$body = json_decode((string) $detail->getContextValue('responseBody'), true);
if ($httpStatus === 422) {
foreach ($body['errors'] ?? [] as $error) {
echo $error['payloadPath'] . ': ' . $error['message'] . "\n";
}
}
}The SDK retries rate limits and server errors on its own, as described in Retries. If server errors continue while you submit an invoice, the call returns an object instead of a JSON string, and its getStatus() is queued: the invoice has not been validated yet, so do not record it as sent.
401: the API key is missing, invalid, revoked or expired. Check that COMPLYANCE_API_KEY is set where your application runs.
403: the key belongs to a different environment. Use a sandbox key with Environment::SANDBOX, and a production key with Environment::PRODUCTION.
422: the invoice failed validation. Each entry in errors names the field in your payload (payloadPath), the field in GETS (getsPath) and the rule it broke. Fix every one, then send the invoice again. With GETS-HEAD-010, the invoice number was already sent: use a new one.
Every status and what to do about it is in Errors.
Next, check which fields your invoices need in the field reference.