# 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.

> **Before you start**
>
> - PHP 7.4 or later, with the `curl` and `json` extensions.
> - [Composer](https://getcomposer.org/) in your project.
> - A Complyance workspace with access to the One Platform Developer portal.
> - A sandbox API key, saved in the `COMPLYANCE_API_KEY` environment variable. See [API keys](https://docs.complyance.io/one-platform/developer-portal/api-keys/). Start in sandbox, and move to production only when your invoices pass there.

## Install

```bash title="Install the SDK"
composer require io.complyance/unify-sdk:3.0.11
```

Version 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 title="Configure the SDK"
<?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::SANDBOX` with a sandbox key. When you go live, use `Environment::PRODUCTION` with a production key.
- `$sourceName` and `$sourceVersion` name 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](https://docs.complyance.io/one-platform/developer-portal/source-management/).
- Never write the key into your code. If `COMPLYANCE_API_KEY` is 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`:

```php title="Choose a retry configuration"
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](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/#purpose).

```php title="Send a sample payload"
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](https://docs.complyance.io/api-reference/unify/submit-and-retrieve/). Pass it in every call on this page. Without it, the SDK sends the earlier format.

You should see:

```json title="Payload saved"
{
  "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](https://docs.complyance.io/one-platform/developer-portal/integration-engine/create/) and [Map your fields to GETS](https://docs.complyance.io/one-platform/developer-portal/integration-engine/gets-mapping/).

## 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.

```php title="Submit an invoice"
$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](https://docs.complyance.io/api-reference/gets/fields/).

You should see:

```json title="Invoice validated"
{
  "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](https://docs.complyance.io/api-reference/unify/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](https://docs.complyance.io/one-platform/developer-portal/integration-engine/test/), 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`:

```php title="Check the status"
$status = GETSUnifySDK::getDocumentStatus($documentId);

echo $status['data']['state'];
```

```json title="Invoice accepted"
{
  "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](https://docs.complyance.io/api-reference/unify/documents/).

## Retrieve purchase invoices

To read invoices your suppliers sent you, call the API directly with the same key: [Get a purchase invoice](https://docs.complyance.io/api-reference/purchases/get/) by its `documentId`, or [List purchase invoices](https://docs.complyance.io/api-reference/purchases/list/) 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:

```php title="Handle errors"
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](https://docs.complyance.io/sdks/php/#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](https://docs.complyance.io/api-reference/unify/errors/).

Next, check which fields your invoices need in the [field reference](https://docs.complyance.io/api-reference/gets/fields/).

Source: https://docs.complyance.io/sdks/php/
