EGS Units
EGS (E-Invoicing Generation Solution) units represent a single device or branch that issues invoices. Each unit is created in either sandbox or production (based on your account at creation time). Sandbox units only submit to ZATCA sandbox; production units only to ZATCA production. Create a unit to start onboarding: Phase 1 (simplified only) or Phase 2 (full). After compliance check, request production CSID with the ZATCA OTP. Use the returned egs_unit_id in invoice requests (or set it as the default on an API key). Status response includes environment (sandbox | production). EGS units cannot be deleted: they are registered with ZATCA and linked to invoice history for compliance and audit. Sandbox and production EGS units have separate quotas per plan (e.g. Solo: 5 sandbox + 1 production), so using your sandbox quota does not block creating production units.
ZATCA compliance during onboarding
When you create an EGS unit with an OTP, Esnad runs the ZATCA compliance check automatically. You do not need a separate compliance API call for normal integrations — poll GET /v1/egs/:id/status until onboarding_status is active, then start submitting invoices.
Create EGS unit
Creates an EGS unit and starts async onboarding (Phase 1 or 2). The unit's environment (sandbox or production) is determined by your API key: use a key starting with zatca_test_ for sandbox, zatca_live_ for production. You can pass environment explicitly; it must match the key or the request returns 400 ENVIRONMENT_KEY_MISMATCH. CSR is generated and ZATCA compliance check runs automatically. Poll the status URL for onboarding progress.
Request body
| Attribute | Type | Presence | Description |
|---|---|---|---|
| unit_name | string | Required | e.g. Main Branch POS |
| invoice_type | "standard" | "simplified" | "both" | Optional | Default: simplified |
| otp | string | Optional | ZATCA OTP for compliance (sandbox: e.g. 123345; production: from Fatoora portal) |
| phase | 1 | 2 | Optional | 1 = Phase 1 (simplified only), 2 = Phase 2 (full) |
| environment | "sandbox" | "production" | Optional | Must match API key: sandbox for zatca_test_ keys, production for zatca_live_. When omitted, backend uses the key's mode. If provided and mismatched, returns 400 ENVIRONMENT_KEY_MISMATCH. |
Code examples
const res = await fetch('https://api.esnadapi.com/v1/egs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
unit_name: 'Main Branch POS',
invoice_type: 'simplified',
phase: 2,
otp: 'optional-zatca-otp',
environment: 'sandbox', // optional; must match API key (zatca_test_ → sandbox, zatca_live_ → production)
}),
});
const { egs_unit_id, poll_url } = await res.json();Success response (200)
{
"egs_unit_id": "uuid",
"serial_number": "SN-xxx",
"onboarding_status": "pending",
"message": "CSR generated. ZATCA compliance check running automatically.",
"poll_url": "/v1/egs/uuid/status"
}EGS onboarding status
Returns onboarding status, cert expiry, and invoice counter. Poll this after creating an EGS unit until onboarding_status is active.
Code examples
const res = await fetch(`${BASE}/v1/egs/${egsUnitId}/status`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const status = await res.json();Success response (200)
{
"egs_unit_id": "uuid",
"unit_name": "Main Branch POS",
"environment": "sandbox",
"phase": 2,
"onboarding_status": "active",
"cert_issued_at": "2025-01-15T10:00:00.000Z",
"cert_expires_at": "2026-01-15T10:00:00.000Z",
"days_until_expiry": 330,
"cert_health": "healthy",
"invoice_counter": 0
}Request production CSID (Phase 2)
After compliance check passes, call this with the ZATCA OTP to obtain production CSID. Cert expiry is returned.
Request body
| Attribute | Type | Presence | Description |
|---|---|---|---|
| otp | string | Required | ZATCA OTP for production CSID |
Code examples
await fetch(`${BASE}/v1/egs/${egsUnitId}/request-production`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ otp: 'zatca-production-otp' }),
});Success response (200)
{
"message": "Production CSID obtained",
"cert_expires_at": "2026-01-15T10:00:00.000Z"
}Renew production CSID
Alias for request-production. Use to renew the production CSID with a new ZATCA OTP.
Request body
| Attribute | Type | Presence | Description |
|---|---|---|---|
| otp | string | Required | ZATCA OTP for production CSID |
Code examples
await fetch(`${BASE}/v1/egs/${egsUnitId}/renew`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ otp: 'zatca-renewal-otp' }),
});Success response (200)
{
"message": "Production CSID obtained",
"cert_expires_at": "2026-01-15T10:00:00.000Z"
}