Best Practices
These are the rules that separate integrations that run for years from integrations that page their developers at month-end. Each one is a lesson learned from production EFRIS deployments.
Critical rules
-
Always register first: never fiscalise a sale with an
itemCodethat hasn't been registered. -
No auto-retry on invoices: if fiscalisation times out, call
POST /invoice-detailsfirst before retrying. Duplicate fiscalisation = double tax. SettingsellerDetails.isCheckReferenceNo: "1"makes EFRIS reject duplicates of yourreferenceNo. -
Stock before sale: ensure stock is increased before selling, or enable negative stock in
the URA portal settings (error
1600: Inventory shortage). -
Packaging fields: if a good was registered with
pieceScaledValue > 1, send the matchingpack/stickvalues on excise lines and keep quantities consistent so URA stock decrements correctly. -
camelCase only: field names must match the official URA names exactly -
invoiceNo,buyerTin,goodsCode,sourceBranchId,deemedFlag,vatProjectId,totalWeight,hsCode. snake_case variants are rejected by validation. -
Amount consistency: per tax line,
netAmount + taxAmountmust equalgrossAmount(error1342), andsummarytotals must equal the sums oftaxDetails(excise-duty lines are excluded from the gross total check).
Invoice type quick reference
| Invoice type | invoiceIndustryCode |
Extra fields needed |
|---|---|---|
| Normal sale | 101 (or omit) |
None |
| Export goods | 102 |
deliveryTermsCode, per-item totalWeight (mandatory),
pieceQty, pieceMeasureUnit, hsCode
|
| Export service | 112 |
deliveryTermsCode |
| Imported service | 104 |
importServicesSeller block, sellerDetails.branchId |
| Airline | 109 |
airlineGoodsDetails array |
| EDC / Fuel | 110 |
edcDetails block (tankNo, pumpNo, nozzleNo
required) |
| Deemed VAT | 101 + deemedFlag: "1" per item |
deemedExemptCode, vatProjectId, vatProjectName per item
|
| Excise duty | 101 + exciseFlag: "1" per item |
categoryId, categoryName, exciseRate,
exciseRule, exciseTax per item (+ pack,
stick, exciseUnit, exciseCurrency when rule 2)
|
| Debit note | any + invoiceType: "4" |
oriInvoiceId, extend.reason / extend.reasonCode |
Recommended integration checklist
- Cache the system dictionary and
commodity categories locally; refresh when
/loginreports a new dictionary version. - Store
invoiceId,invoiceNo(FDN),antifakeCode, andqrCodeagainst every ERP invoice record - see Invoice Response. - Log the full request and response of every API call. When URA support asks "what did you send?", you want an answer.
- Treat
response: "ERROR"with an EFRIS code inmessageas a business error to surface to the user, not as a transient failure to retry. - Run a daily reconciliation: compare your ERP's fiscalised invoices against
POST /query-invoicesfor the same date range.