efaturaefatura
Beta

Beta documentation. APIs may change before the stable release.

API Reference

The root package exports a framework-agnostic facade through createEfatura(config, dependencies). The facade validates domain payloads, builds XML, signs documents, packages ZIP payloads, renders DFA PDFs, and delegates transport calls to injected infrastructure.

import { createEfatura } from '@akira-io/efatura';

const efatura = createEfatura(config, dependencies);

Configuration And Dependencies

config identifies the transmitter, software, base URLs, and environment. dependencies replaces infrastructure contracts such as sequence storage, XML signing, XSD validation, DFA rendering, and fiscal authority clients.

See Configuration and Storage for setup details.

Invoice Builder

const invoice = efatura
  .invoice()
  .type('FTE')
  .issueDate('2026-02-08')
  .receiver({ taxId: { countryCode: 'CV', value: '900800700' }, name: 'Receiver' })
  .line({
    quantity: { value: 1, unitCode: 'UN' },
    price: 1000,
    priceExtension: 1000,
    netTotal: 1000,
    taxes: [{ taxTypeCode: 'IVA', taxPercentage: 15, taxTotal: 150 }],
    item: { description: 'Service', emitterIdentification: 'SERV-001' },
  })
  .totals({
    priceExtensionTotalAmount: 1000,
    netTotalAmount: 1000,
    taxTotalAmount: 150,
    payableAmount: 1150,
  })
  .validate();

validate() returns normalized InvoiceData or throws EfaturaValidationError.

Numbering And IDs

MethodPurpose
generateSubmissionId()Creates a submission identifier from configured generators
generateBatchId()Creates a batch identifier from configured generators
nextDocumentNumber(issueDate, documentType)Allocates the next sequence number for the configured transmitter and LED
buildIud(input)Builds an IUD from explicit document data
buildSequentialIud(input)Allocates the next number, then builds the IUD
buildEventId(input)Builds the official 24-character Event ID

buildIud() fills repositoryCode, emitterNif, and led from configuration when they are omitted.

XML

const xml = efatura.buildDfeXml(invoice, {
  documentNumber: 1,
  randomCode: '1234567890',
  emissionMode: 'Online',
});

buildDfeXml(data, options) accepts raw invoice records or normalized InvoiceData. It validates the invoice, validates issue-date tolerance, checks contingency consistency, creates or uses the IUD, then returns DFE XML.

Options:

OptionPurpose
iudUse an existing IUD instead of generating one
documentNumberRequired when iud is not supplied
randomCodeOptional IUD random code
emissionModeOnline, Offline, or Off

Validation helpers:

await efatura.validateDfeXml(xml, 'FTE');
await efatura.validateEventXml(eventXml);

XSD validation uses the configured xsdValidator. Without a concrete validator, the default missing validator reports that official XSD validation is not configured.

Currency Conversion

Facade Method

const prepared = await efatura.prepareInvoiceToCve(invoiceInEur, {
  sourceCurrency: 'EUR',
  effectiveAt: new Date('2026-07-21T11:30:00.000Z'),
  rateType: 'buy',
});

prepareInvoiceToCve(data, options): Promise<PreparedCurrencyInvoice> accepts a raw invoice record or InvoiceData. It validates the source, resolves one quote targeting CVE, returns a new converted invoice, and validates that projection again. It does not mutate the input.

PrepareInvoiceToCveOptions:

FieldTypeRequiredDefault
sourceCurrencystringyesnone; code from the active e-Fatura XSD currency list, trimmed and uppercased
effectiveAtDatenoinvoice issue date and explicit issue time; configured clock time in Cape Verde when issue time is absent
rateTypeExchangeRateTypenoprovider-specific; BCV uses buy

PreparedCurrencyInvoice:

FieldTypeMeaning
invoiceInvoiceDataNew normalized CVE projection
conversionCurrencyConversionMetadataQuote plus original and converted payable values

CurrencyConversionMetadata extends ExchangeRateQuote with originalPayableAmount and convertedPayableAmount, both numbers.

Provider Contract

type ExchangeRateType = 'buy' | 'sell' | 'reference' | 'custom';

interface ExchangeRateProvider {
  getQuote(request: ExchangeRateRequest): Promise<ExchangeRateQuote>;
}

ExchangeRateRequest fields:

FieldTypeRequiredMeaning
sourceCurrencystringyesCurrency multiplied by the rate
targetCurrencystringyesResult currency; fiscal preparation fixes this to CVE
effectiveAtDateyesLatest acceptable quote date
rateTypeExchangeRateTypenoRequested buy, sell, reference, or custom type

ExchangeRateQuote fields:

FieldTypeRequiredMeaning
sourceCurrencystringyesNormalized source code
targetCurrencystringyesNormalized target code
ratenumberyesPositive source-to-target multiplier, normalized to five fractional digits
rateTypeExchangeRateTypeyesApplied quote type
effectiveAtDateyesPublication or observation date
retrievedAtDateyesTime the source was retrieved
providerstringyesNon-empty source identity
sourceUrlstringnoHTTPS evidence URL without user information
evidenceExchangeRateEvidencenoStructured observation evidence supplied by the provider

ExchangeRateEvidence records the source, indicator, observation period, and legs. Each ExchangeRateEvidenceLeg records a source or target role, canonical currency, optional economy, exact positive decimal value, and optional HTTPS observation URL. World Bank quotes use this field to preserve the exact cross-rate inputs and endpoints. ExchangeRateEvidenceLegRole is source or target.

Rate direction is amountInTarget = amountInSource * rate.

normalizeCurrencyCode(currencyCode) trims and uppercases a code, then checks a runtime set copied from the active embedded ISO_ISO3AlphaCurrencyCode_2012-08-31.xsd enumeration. The package does not read the schema at runtime. The XSD contains 179 entries: 178 canonical uppercase codes plus the anomalous mixed-case IdR. The exhaustive test requires IdR to remain the only noncanonical entry and compares the checked-in set with all 178 usable codes. Canonical IDR is rejected because the active XSD rejects it, and the package does not emit noncanonical IdR. Schema-listed special codes such as XAU, XTS, and XXX remain accepted. payableAlternativeAmountSchema uses the same canonical set for low-level invoice input. Host Intl support does not change this contract. Unsupported provider and quote inputs fail with exchange_rate.currency_unsupported; unsupported low-level alternative amount codes fail with validation.payable_alternative_currency_unsupported. validateExchangeRateQuote(request, quote) applies the same currency check before it verifies the pair, requested rate type, dates, provider, optional HTTPS URLs without user information, structured evidence, and positive rate.

Set EfaturaDependencies.exchangeRateProvider in the second createEfatura() argument to replace the provider. Omission constructs BcvExchangeRateProvider with the facade clock.

BcvExchangeRateProvider

new BcvExchangeRateProvider(options?: BcvExchangeRateProviderOptions)
OptionTypeDefault
fetchertypeof fetchglobal fetch
clockClockSystemClock
sourceUrlstringofficial BCV dated print page
timeoutMsnumber10000
maxResponseBytesnumber1048576
allowPreviousPublicationbooleanfalse
maxPublicationAgeDaysnumber0

getQuote() supports buy and sell quotes targeting CVE and defaults to buy when called without a rate type. It requires exactly one rate table and one anchored spanning publication row, with one adjacent semantic-heading compatibility shape when the spanning row is absent. It normalizes rates published for multiple units and compares the requested instant by the fixed UTC-01 Cape Verde calendar date. timeoutMs and maxResponseBytes must be positive safe integers; maxPublicationAgeDays must be a nonnegative safe integer. Previous publications require both an enabled policy and an adequate maximum age. The current BCV page is dynamic and is not a documented historical API.

The sourceUrl origin must be exactly https://www.bcv.cv, without credentials or a non-default port. Automatic redirects are disabled, and redirect responses fail with exchange_rate.provider_unavailable. Use FixedExchangeRateProvider or CallbackExchangeRateProvider for another approved source.

WorldBankExchangeRateProvider

new WorldBankExchangeRateProvider(options?: WorldBankExchangeRateProviderOptions)
OptionTypeDefault
fetchertypeof fetchglobal fetch
clockClockSystemClock
economyByCurrencyReadonly<Record<string, string>>{ CVE: 'CPV' }
indicatorstringPA.NUS.FCRF
baseUrlstringhttps://api.worldbank.org
timeoutMsnumber10000
maxResponseBytesnumber1048576

The provider supports reference quotes only and uses annual observations for one UTC year. The CVE mapping is locked to CPV. USD needs no mapping; other source currencies need an explicit economy mapping. Only PA.NUS.FCRF and the official https://api.worldbank.org origin are accepted, preventing false World Bank attribution. Returned evidence preserves the period, economy, endpoint, role, currency, and exact leg value. World Bank is an annual reference source, not a fiscal daily default.

FixedExchangeRateProvider

new FixedExchangeRateProvider(options: FixedExchangeRateProviderOptions)
OptionTypeRequiredDefault
sourceCurrencystringyesnone
targetCurrencystringyesnone
ratenumberyesnone
effectiveAtDateyesnone
retrievedAtDatenoeffectiveAt
rateTypeExchangeRateTypenocustom
providerstringyesnone
sourceUrlstringnoomitted

The constructor validates and stores one quote. getQuote() rejects a different pair, rate type, or an effective date earlier than the stored quote.

CallbackExchangeRateProvider

type ExchangeRateCallback = (
  request: ExchangeRateRequest,
) => Promise<ExchangeRateQuote>;

new CallbackExchangeRateProvider(callback: ExchangeRateCallback)

The callback result is validated against the request. Existing ExchangeRateError instances pass through. Other callback failures become exchange_rate.provider_unavailable with the original failure as the cause.

ExchangeRateError

new ExchangeRateError(
  code: ExchangeRateErrorCode,
  message: string,
  options?: { cause?: unknown },
)

ExchangeRateErrorCode contains:

CodeMeaning
exchange_rate.provider_unavailableProvider transport or callback failed
exchange_rate.response_invalidProvider content or quote shape is invalid
exchange_rate.currency_unsupportedInput is absent from the active e-Fatura XSD currency list, or the provider lacks its required mapping
exchange_rate.pair_mismatchPair or requested rate type does not match
exchange_rate.rate_invalidRate is non-positive, non-finite, or rounds to zero
exchange_rate.date_unavailableQuote is later than requested or no allowed date exists
exchange_rate.date_invalidRequested or returned date is invalid
exchange_rate.staleBCV publication exceeds maximum age
exchange_rate.source_requiredRequired source configuration is not valid HTTPS provenance
exchange_rate.invoice_invalidConverted projection fails fiscal validation
exchange_rate.alternatives_conflictExisting alternative amounts prevent safe conversion

No provider silently falls back to another source. See Currency Conversion for provider policy, monetary fields, audit storage, and recovery decisions.

Events

const eventXml = efatura.buildEventXml({
  type: 'FDC',
  issueDateTime: '2026-02-08T11:30:00',
  issueReasonDescription: 'Documento emitido com dados incorretos.',
  iuds: [iud],
});

validateEvent(data) normalizes event payloads. buildEventXml(data, options) builds official Event XML for FDC and UDN. See Events.

Signing And Certificates

const signed = await efatura.signDfeXml(xml, {
  certificate: process.env.EFATURA_CERTIFICATE_PEM,
  privateKey: process.env.EFATURA_PRIVATE_KEY_PEM,
});
MethodPurpose
signDfeXml(xml, options)Signs DFE XML with the configured signer
signEventXml(xml, options)Signs Event XML with the configured signer
validateCertificate(input)Validates certificate material through the configured certificate validator

The default XAdES-BES signer requires certificate and private key material. See Signing And Certificates.

ZIP And Submission

const zip = efatura.buildDfeZip([{ iud, xml: signed.xml }]);
const result = await efatura.submitDfeZip(zip);
MethodPurpose
buildDfeZip(files)Creates a Deflate ZIP with {IUD}.xml entries
submitDfeZip(zip)Submits through the configured middleware transport
submitDfeZipToPlatform(zip, options)Submits through the configured platform transport

submitDfeZip() requires transmitterKey in config. submitDfeZipToPlatform() requires an OAuth access token in options.

See Packaging.

DFA

const document = await efatura.renderDfa({
  iud,
  invoice,
});
MethodPurpose
dfaQrCodeUrl(iud)Builds the official DFA lookup URL from dfaBaseUrl
renderDfa(options)Renders a PDF through the configured DFA renderer

RenderDfaOptions.conversion?: CurrencyConversionMetadata supplies conversion evidence to the default or custom renderer. DfaRenderInput.conversion exposes the same optional field. Direct metadata requires an invoice with totals, a CVE target, a converted payable value equal to invoice totals and to the original payable amount multiplied by the normalized rate with two-decimal half-up rounding, and foreign original value, currency, and rate equal to the sole alternative payable amount. Invalid metadata throws dfa.conversion_invalid.

RenderDfaOptions.currency is deprecated because fiscal values are always CVE and will be removed in v1.0.0. Supplying it emits one Node.js DeprecationWarning per process with code EFATURA_RENDER_DFA_CURRENCY_DEPRECATED, including when an invoice causes the legacy label to be ignored. A foreign value in IUD-only rendering still throws dfa.currency_invalid after the warning. Remove the option. For foreign invoices, use prepareInvoiceToCve() and pass the returned invoice and conversion values to renderDfa().

The default renderer returns:

{
  contentType: 'application/pdf',
  filename: `${iud}.pdf`,
  buffer: Buffer
}

See DFA.

Fiscal Readiness

const readiness = await efatura.validateFiscalReadiness(invoice, {
  accessToken: process.env.EFATURA_ACCESS_TOKEN,
});

Without accessToken, external PE and DNRE checks return skipped. With a token, the configured clients check taxpayer status, software registration, and emitter authorization.

Errors

Domain and presentation failures use EfaturaValidationError. Official artifacts missing from the local package use OfficialArtifactMissingError. Catch these at adapter boundaries and convert them into application-specific HTTP or job errors.