# Authentication and environments

API key headers and environment selection for Connect and document requests.

## API key

Create a key in **API keys** in your workspace’s **Developer portal**. Store the secret in a server-side secret manager and load it as `COMPLYANCE_API_KEY` for these examples. Never include it in browser code, source control, screenshots or support messages.

Your key determines the workspace you can access. Do not add `workspaceId` to a Connect request. The API does not read a client or workspace ID from any header; the `client_id` the Ruby SDK asks for is a setting of the SDK only.

A key is read-only, or read and write. Requests that change something, such as submitting a document, need write permission. A key also has an expiry date, after which it stops working.

## Headers

| API | Required authentication header |
| - | - |
| `/v3/connect/*` | `X-API-Key: YOUR_API_KEY` |
| `/api/v3/unify` and `/api/v3/documents*` | `Authorization: Bearer YOUR_API_KEY` |

Connect, including the credit API, also accepts the key as `Authorization: Bearer YOUR_API_KEY`. The examples in these docs use the headers in the table.

Send `Content-Type: application/json` with a JSON body. Use `Accept: application/json` for JSON responses.

```bash title="Read your company list"
curl 'https://prod.gets.complyance.io/v3/connect/companies?limit=20' \
  --header "X-API-Key: ${COMPLYANCE_API_KEY}" \
  --header 'Accept: application/json'
```

## Environments

| Operation | Selector | Accepted values |
| - | - | - |
| Create a UAE company | Body `platformEnvironment` | `sandbox`, `simulation`, `production` |
| Submit through Unify | Body `environment` | `sandbox`, `production` |
| Read document counts | Header `X-Environment` | `sandbox`, `production`; defaults to `production` |
| Change credit allowances | Header `X-Environment` | `sandbox`, `production`; defaults to `production` |
| Retrieve purchases | API key’s environment | Use the key for the environment where the invoice was received |

`simulation` is a third test environment that some tax authorities run, such as ZATCA’s in Saudi Arabia; see [Test in sandbox](https://docs.complyance.io/get-started/sandbox/). Unify does not accept it, so onboard a company in `sandbox` to send test invoices.

A sandbox key is also called a mock key: an error that says a mock API key is required means the request needs a sandbox key.

Use `sandbox` throughout the [first-invoice walkthrough](https://docs.complyance.io/partner-platform/isv/first-uae-invoice/connect/). Send `X-Environment` explicitly when requesting counts or changing credits. A successful sandbox request does not demonstrate production readiness: see [Test in sandbox](https://docs.complyance.io/get-started/sandbox/), then follow the go-live checklist for your country.

## Authentication failures

`401` means authentication failed. Check the header name, the key value and whether the key is still active. A `403` means the request is not permitted for that workspace or resource; changing identifiers does not grant access.

Connect and document requests return a `401` without a `success`, `error` or `meta` wrapper when the key is wrong. `code` is `invalid_api_key` for a key that does not exist, `key_revoked` for a revoked key and `key_expired` for an expired one:

```json title="Invalid API key — HTTP 401"
{
  "code": "invalid_api_key",
  "message": "Invalid API key"
}
```

When no key is sent at all, the answer depends on the endpoint:

| Endpoint | `401` body |
| - | - |
| `/api/v3/documents` and `/api/v3/documents/{documentId}` | JSON: `{"code": "authentication_required", "message": "Authentication required"}` |
| `/v3/connect/*` and `/api/v3/documents/{documentId}/status` | Plain text: `Authentication required` |

So check the status code and the `Content-Type` before you parse a `401` body. On Connect, a `401` with `CONNECT_AUTH_CONTEXT_MISSING` comes inside the usual [Connect error envelope](https://docs.complyance.io/api-reference/connect/errors/): the key was accepted, but no workspace could be found for it. Email <support@complyance.io> with `meta.requestId`.

Unify returns the same `401` for every authentication failure, whether the key is missing, invalid, revoked or expired:

```json title="Unify authentication failure — HTTP 401"
{
  "error": "Unauthorized"
}
```

A Unify `403` with `permission_denied` means the key belongs to a different environment from the request’s `environment`: use a sandbox key with `sandbox` and a production key with `production`. A `403` with `insufficient_permission` means the key is read-only and the request needs write permission.

Keep the response’s request identifier, when there is one, for a support request to <support@complyance.io.> Remove the API key from any request you share.

Source: https://docs.complyance.io/api-reference/authentication/
