Examples
End-to-end flows you can copy and adapt. Each one goes from raw input to a document the SII has accepted, including storage, retries and error handling.
These examples use the planned 1.0 API. The published 0.0.2 is a
placeholder, and details may change before 1.0.
A type 33 with a taxable and an exempt line: build, stamp, sign, send, and poll the status from a job.
Issue a Credit NoteA type 61 that references invoice 1024 to void it (code 1) or correct its amounts (code 3).
Start with the invoice: the credit note reuses its modules and corrects the invoice it issues.
Three Rules Both Examples Follow#
Never Reuse a Folio#
Every document of a type gets its own folio from the CAF range. Allocate it in the same transaction that stores the document, so a validation error doesn't burn one:
const signed = await db.transaction(async (tx) => {
const folio = await tx.folios.next(33)
const signed = await buildInvoice({ folio, receiver, items })
await tx.invoices.insert({ folio, xml: signed.xml, state: 'signed' })
return signed
})Store Before You Send#
The signed XML is the legal document. Persist it first, then send, then persist the track id:
const { trackId } = await sendToSii(signed, { certificate, ...SII })
await db.invoices.update(signed.folio, { trackId, state: 'sent' })Branch on the Code#
Every failure is a DteError with a stable code. Messages are generic and never include RUTs or amounts, so don't parse them:
import { DteError } from 'dte-sdk'
if (error instanceof DteError && error.code === 'SII_UNAVAILABLE') {
// keep the document as 'signed' and retry the upload later
}Run both examples with SII_ENVIRONMENT=certification until the SII
authorizes you for production. The code is the same in both environments.