@open-nav/evat
v0.3.0
Published
Client for NAV's eÁFA (eVAT) M2M VAT-return interface
Downloads
626
Maintainers
Readme
@open-nav/evat
Client for NAV's eÁFA (eVAT) M2M interface — Hungary's electronic VAT-return system (EAR 2.0), distinct from Online Számla.
npm install @open-nav/evatGenerated types and serialisation metadata come from the vendored
schemas/EAR/2.0/ XSDs (see scripts/vendor_schemas.py); the schemas are
vendored under NAV's evident org-wide MIT intent, though the upstream repository
states no licence (see schemas/NOTICE.md).
Access
eÁFA reuses the Online Számla technical user — same login, password and signature key, no separate credentials. Grant that technical user two things in the NAV portals: "Hozzáférés e-ÁFA rendszer interfészhez" (interface access, in the User Manager) and, for the query operations, the "eÁFA M2M Lekérdezés" role. Authentication is the same crypto as Online Számla (SHA-512 password hash, SHA3-512 request signature).
import { EvatClient } from '@open-nav/evat';
const client = new EvatClient({
environment: 'test', // or 'production'
credentials: {
login: process.env.NAV_LOGIN!,
password: process.env.NAV_PASSWORD!,
signKey: process.env.NAV_SIGN_KEY!,
taxNumber: '12345678', // the 8-digit core
},
software: {
softwareId: 'MYCOMPANY000000001',
softwareName: 'my app',
softwareOperation: 'LOCAL_SOFTWARE',
softwareMainVersion: '1.0.0',
softwareDevName: 'My Company',
softwareDevContact: '[email protected]',
softwareDevCountryCode: 'HU',
softwareDevTaxNumber: '12345678',
},
});Reading VAT returns
import { decodeDownloadPayload } from '@open-nav/evat';
// Discovery — find the returns filed in a window (max 35 days per query).
const list = await client.queryDeclarationList({
taxpointDateFrom: '2026-01-01',
taxpointDateTo: '2026-01-31',
});
// Content — download and decode a return's compiled data, in one call.
import { readVatDeclaration } from '@open-nav/evat';
const xml = await readVatDeclaration(client, declarationProcessingId); // VatDeclarationData XMLreadVatDeclaration wraps queryVatDeclarationData (a multipart/form-data
download with a gzipped payload part) and decodeDownloadPayload (the gunzip).
The whole read path — discovery and content — is verified against NAV's eÁFA
test system.
declarationList vs statementList
queryDeclarationList returns two lists, and this trips people up: the
analytics-based M2M declarations are in declarationList, while the
traditional VAT returns (bevallás) — a taxpayer's regular monthly returns
filed through ÖNYA/ÁNYK — are in statementList. If a query "returns nothing",
you are probably reading the wrong one.
import { queryAllDeclarations, queryAllStatements } from '@open-nav/evat';
const declarations = await queryAllDeclarations(client, range); // declarationList
const statements = await queryAllStatements(client, range); // statementListqueryDeclarationList caps a query window at 35 days; both helpers above
walk the 35-day windows for you (and chunkTaxpointRange exposes the split).
Documents
queryDocumentList is asynchronous — it returns a queryId you resolve with
queryDocumentListResult. queryAllDocuments does both, polling until the
result is ready, across the 35-day windows:
import { queryAllDocuments } from '@open-nav/evat';
const lists = await queryAllDocuments(client, range); // one DocumentListType per windowFiling a declaration
const result = await client.submitDeclaration({
declarationXml, // a VatDeclarationData document
xsdVersion: 'eardata_2.0', // the current, live XSD version
requestPeriodStart: '2026-01-01',
requestPeriodEnd: '2026-01-31',
});
// poll queryDeclarationProcessingStatus(result.declarationProcessingId) to FINISHED,
// then manageDeclarationSubmission(...) to file.submitDeclaration runs the upload lifecycle — manageDeclarationUpload (with
the SHA3-512 content hash) → gzipped manageDeclarationPartition(s) →
manageDeclarationFinalize. Polling and the final manageDeclarationSubmission
are separate so you control the wait and the approval. The first analytics for a
period must be version 1; attachment claimCheckIds in the XML must match
those uploaded via manageAttachmentUpload. This lifecycle is verified live end
to end.
buildVatAnalytics / buildVatAnalyticsItem bridge open-nav invoice data into
the declaration's VAT-analytics ledger; classifying each line to a standard tax
code is left to you (a VAT-law judgement), with TAX_CODES to draw on.
Testing without credentials
createEvatMock() returns a fetch to hand to EvatClient via
transport.fetch, driving the whole lifecycle — upload, partitions, finalize,
status polling, submission and the queries — in-process with no technical user.
import { createEvatMock, EvatClient } from '@open-nav/evat';
const mock = createEvatMock({ credentials });
const client = new EvatClient({ credentials, software, transport: { fetch: mock.fetch } });Licence
MIT. Not affiliated with NAV.
