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.

POST/v1/egs

Create EGS unit

Creates an EGS unit and starts async onboarding (Phase 1 or 2). CSR is generated and ZATCA compliance check runs automatically. Poll the status URL for onboarding progress.

Requires API Key (Bearer)

Request body

CreateEgsDto
FieldTypeRequiredDescription
unit_namestringYese.g. Main Branch POS
invoice_type"standard" | "simplified" | "both"NoDefault: simplified
otpstringNoZATCA OTP for compliance
phase1 | 2No1 = Phase 1 (simplified only), 2 = Phase 2 (full)

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',
  }),
});
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"
}
GET/v1/egs/:id/status

EGS onboarding status

Returns onboarding status, cert expiry, and invoice counter. Poll this after creating an EGS unit until onboarding_status is active.

Requires API Key (Bearer)

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
}
POST/v1/egs/:id/request-production

Request production CSID (Phase 2)

After compliance check passes, call this with the ZATCA OTP to obtain production CSID. Cert expiry is returned.

Requires API Key (Bearer)

Request body

RequestProductionDto
FieldTypeRequiredDescription
otpstringYesZATCA 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"
}
POST/v1/egs/:id/renew

Renew production CSID

Alias for request-production. Use to renew the production CSID with a new ZATCA OTP.

Requires API Key (Bearer)

Request body

RequestProductionDto
FieldTypeRequiredDescription
otpstringYesZATCA 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"
}