Skip to content

Documents

A document starts as plain data that you describe in readable English (issuer.businessName, items[0].unitPrice) and ends as the XML the SII expects. createDocument is the first step: it validates what you give it, normalizes RUTs and text, numbers the lines, computes the totals and returns a Document. issueDocument runs the whole pipeline (create, stamp, sign) in one call. buildDocumentXml and the codec (toSiiDocument, fromSiiDocument) turn the model into SII tags and back.

Everything on this page lives in the dte-sdk entry point and runs on Node, Bun and Deno.

Documents, stamps, signatures and envelopes are tested against synthetic data and against the official schemas, but they have not been sent to the real SII yet. See Status for what has met the real SII and what has not.

Create a document#

TypeScript
import { createDocument } from 'dte-sdk'
 
const document = createDocument({
  type: 33,
  folio: 1,
  issuer: {
    rut: '76543210-3',
    businessName: 'Comercial Andina SpA',
    businessActivity: 'Software',
    address: 'Av. Los Aromos 1540',
    commune: 'Santiago',
    activityCode: 620200,
  },
  receiver: {
    rut: '12.345.678-5',
    businessName: 'Comercializadora Rapel Ltda',
    businessActivity: 'Comercio',
    address: 'Pasaje Los Olmos 88',
    commune: 'Providencia',
  },
  items: [
    { name: 'Plan anual', quantity: 1, unitPrice: 100000 },
    { name: 'Horas de consultoría', quantity: 3, unitPrice: 15000, discountPercent: 10 },
  ],
})
 
document.id // 'DTE_T33F1'
document.header.receiver.rut // '12345678-5'
document.items[1] // { lineNumber: 2, name: 'Horas de consultoría', quantity: 3, unitPrice: 15000, discountPercent: 10, discountAmount: 4500, amount: 40500 }
document.header.totals // { netAmount: 140500, vatRate: 19, vatAmount: 26695, totalAmount: 167195 }

createDocument is pure: the same input gives the same output, it never mutates its argument, and it touches no network, file or secret. The folio is not checked against a CAF here; that happens when you stamp the document (see Stamp).

Issue in one call#

issueDocument takes a DocumentInput (or a Document you already created), a parsed CAF and a certificate, and returns a signed document:

TypeScript
import { readFileSync } from 'node:fs'
import { issueDocument, loadCertificateFromPkcs12, parseCaf } from 'dte-sdk'
 
const certificate = loadCertificateFromPkcs12(readFileSync('firma.pfx'), process.env.PFX_PASSWORD!)
const caf = parseCaf(readFileSync('caf-33.xml', 'utf8'))
 
const signed = issueDocument({ document: input, caf, certificate })
 
signed.xml // signed <DTE> with its TED, no XML declaration
signed.ted // the electronic stamp
signed.signedAt // Chile time of the signature
OptionMeaning
documentA DocumentInput (it goes through createDocument) or an already created Document.
cafThe parsed CAF that covers the type, the issuer and the folio.
certificateThe issuer certificate that signs the document.
nowClock for the issue date, TSTED and TmstFirma. Tests use it to get deterministic output.
checkCafExpiryCheck the CAF expiry (default true). See Stamp.
timestampTSTED and TmstFirma exactly as given, instead of the Chile time of now.

The result is a SignedDocument: a Document plus ted, xml, signedAt and signature. The steps it runs are the ones documented on Stamp and Sign; call them separately when you need to stop in the middle (to inspect the TED, for example).

Document types#

The SDK supports nine TipoDTE codes. DOCUMENT_TYPES names them in English, DOCUMENT_TYPE_VALUES lists them in ascending order, and isDocumentType(value) is a type guard.

CodeDOCUMENT_TYPES keyOfficial name (documentTypeName)ReceiptExemptNoteNeeds receiverTransferable
33invoiceFactura Electrónicanononoyesyes
34exemptInvoiceFactura Exenta Electrónicanoyesnoyesyes
39receiptBoleta Electrónicayesnononono
41exemptReceiptBoleta Exenta Electrónicayesyesnonono
43settlementInvoiceLiquidación Factura Electrónicanononoyesyes
46purchaseInvoiceFactura de Compra Electrónicanononoyesyes
52dispatchGuideGuía de Despacho Electrónicanononoyesyes
56debitNoteNota de Débito Electrónicanonoyesyesno
61creditNoteNota de Crédito Electrónicanonoyesyesno

The columns are the answers of the helpers of the same name:

Helpertrue forWhat it means
isReceipt(type)39, 41Receipts (boletas): no identified receiver required, no TasaIVA, own reference shape, sent by REST.
isExemptType(type)34, 41Every line is exempt.
isNote(type)56, 61Debit and credit notes, which the SII expects to reference another document.
requiresReceiver(type)everything but 39 and 41The type needs an identified receiver.
isTransferable(type)33, 34, 43, 46, 52The printed document carries a transferable copy (cedible) and the Ley 19.983 acknowledgement box.

documentTypeName(type, variant) returns the official Spanish name. The variant is 'long' (the default, as in the table), 'short' (uppercase, no accents: NOTA CREDITO) or 'printed' (uppercase, as the SII printed-samples manual writes it: NOTA DE CRÉDITO ELECTRÓNICA).

createDocument does not force a note to carry a reference. The SII expects every debit or credit note to reference the document it affects, so add references yourself (see Credit and debit notes).

The DocumentInput model#

DocumentInput is what you give to createDocument. Required fields are in bold in the first table; everything else is optional. Fields named after SII tags in the tables below are the tags the codec writes (TipoDTE, MntTotal…), so you can follow a field from your input to the XML.

FieldTypeMeaning
typeDocumentTypeTipoDTE: one of 33, 34, 39, 41, 43, 46, 52, 56, 61.
folionumberFolio: a positive integer authorized by a CAF.
issueDatestringFchEmis, YYYY-MM-DD (DD-MM-YYYY and DD/MM/YYYY are accepted). Defaults to today in Chile.
issuerIssuerInputWho issues the document.
receiverReceiverInputWho receives it. Required on every type except 39 and 41.
itemsLineItemInput[]The detail lines. At least one.
referencesReferenceInput[]References to other documents (every type except receipts).
receiptReferencesReceiptReferenceInput[]References of a receipt (39 and 41 only).
globalAdjustmentsGlobalAdjustmentInput[]Global discounts and surcharges (DscRcgGlobal).
subtotalsSubtotalInfo[]Informative subtotals (SubTotInfo), written as given.
commissionsCommissionLineInput[]Commission lines of a settlement invoice (43).
transportTransportInputTransport data, for dispatch guides (52).
optionsHeaderOptionsOptional IdDoc fields: payment terms, due date, dispatch and transfer types…
pricesIncludeVatbooleanThe unit prices already include VAT (MntBruto=1).
withholdVatbooleanWithhold all the VAT (ImptoReten 15). true by default on purchase invoices (46).
totalsTotalsExplicit totals (advanced). Computed from the lines when omitted.

Issuer#

The issuer is the company that issues the document (Emisor). The RUT may come in any shape (76.543.210-3, 76543210-3, 765432103); rut.ts validates it and the document keeps it as 76543210-3.

FieldRequiredSII tagLimitNotes
rutyesRUTEmisorValidated with rut.ts.
businessNameyesRznSoc (RznSocEmisor on receipts)100Legal name.
businessActivityyesGiroEmis (GiroEmisor on receipts)80Line of business.
addressyesDirOrigen70
communeyesCmnaOrigen20
citynoCiudadOrigen20Defaults to the commune.
activityCodenoActecoSII economic activity code (a number).
phonenoTelefonoNot written on receipts.
emailnoCorreoEmisorNot written on receipts.
branchnoSucursal20Branch name.
siiBranchCodenoCdgSIISucurBranch code registered at the SII.
salespersonCodenoCdgVendedor60Not written on receipts.

Receiver#

FieldRequiredSII tagLimitNotes
rutyes (if a receiver is given)RUTRecepValidated with rut.ts.
businessNameyes (if a receiver is given)RznSocRecep100
addressyes, except on receiptsDirRecep70
communeyes, except on receiptsCmnaRecep20
businessActivitynoGiroRecep40Not written on receipts.
internalCodenoCdgIntRecep20
contactnoContacto80
emailnoCorreoRecepNot written on receipts.
citynoCiudadRecep20
postalAddressnoDirPostal70Not written on receipts.
postalCommunenoCmnaPostal20Not written on receipts.
postalCitynoCiudadPostal20Not written on receipts.

On a receipt (39, 41) you can leave receiver out. The document then carries the generic final consumer: RUT 66666666-6, name Consumidor Final, address Sin Direccion, commune Santiago. A receiver you do give on a receipt is kept as it is, with only what you wrote (its name stays required).

Items#

Each entry of items is a LineItemInput. The SDK numbers the lines from 1 (NroLinDet) and computes amount (MontoItem) unless you give it.

FieldSII tagLimitMeaning
name (required)NmbItem80Item name.
descriptionDscItem1000Description.
quantityQtyItemQuantity. Counts as 1 in the amount when omitted, but it is not written.
unitUnmdItem4Unit of measure.
unitPricePrcItemUnit price, net or VAT-included according to pricesIncludeVat. Counts as 0 when omitted.
unitPriceDecimalsPrcItemDecimals the price is written with, an integer from 0 to 6. Without it the number is written as it is.
discountPercentDescuentoPctLine discount in percent. The SDK also writes DescuentoMonto.
surchargePercentRecargoPctLine surcharge in percent. The SDK also writes RecargoMonto.
exemptIndExeShorthand for exemptIndicator: 1.
exemptIndicatorIndExe1 exempt, 2 non-billable, 3 container deposit, 4 not a sale, 5 deduction, 6 negative non-billable (EXEMPT_INDICATORS).
codesCdgItemUp to 5 codes: { type: 'INT1', value: 'T-100' }.
additionalTaxCodesCodImpAdicUp to 2 additional tax codes (numbers).
settledDocumentTypeTpoDocLiq3Type of the settled document. Required on every line of a settlement invoice (43).
referenceQuantityQtyRefReference quantity.
referenceUnitUnmdRef4Reference unit.
referencePricePrcRefReference price.
amountMontoItemExplicit line amount. It skips the quantity times price computation.

How the line amount is computed (rounding per line, discounts, surcharges) is on Totals.

An item is exempt when you set exempt: true, set an exemptIndicator, or when the document type itself is exempt (34 and 41 give every line IndExe=1 unless you set another indicator). The SDK does not check codes or additionalTaxCodes for length: they are written as you give them.

Prices with decimals need unitPriceDecimals, because the amount is always whole pesos but the SII reads PrcItem as written:

TypeScript
{ name: 'Tornillo', quantity: 3, unit: 'UN', unitPrice: 12.5, unitPriceDecimals: 2 }
// PrcItem is written as 12.50, MontoItem as 38 (round(3 × 12.5) = round(37.5))

Credit and debit notes with references#

references links the document to others. For credit and debit notes (61, 56) it is how you say which document you are cancelling or correcting.

TypeScript
import { createDocument, REFERENCE_CODES } from 'dte-sdk'
 
const creditNote = createDocument({
  type: 61,
  folio: 1,
  issuer,
  receiver,
  items: [{ name: 'Plan anual', quantity: 1, unitPrice: 100000 }],
  references: [
    {
      documentType: 33,
      folio: 1,
      date: '2026-11-01',
      code: REFERENCE_CODES.cancels,
      reason: 'Anula factura',
    },
  ],
})
 
creditNote.references
// [{ lineNumber: 1, documentType: 33, folio: '1', date: '2026-11-01', code: 1, reason: 'Anula factura' }]
creditNote.header.totals // { netAmount: 100000, vatRate: 19, vatAmount: 19000, totalAmount: 119000 }
FieldSII tagLimitMeaning
documentTypeTpoDocRefType of the referenced document (a number), or the string 'SET' for certification test sets.
folioFolioRefFolio of the referenced document (a number or a string; it is stored as a string).
dateFchRefDate of the referenced document, YYYY-MM-DD (or DD-MM-YYYY).
codeCodRefREFERENCE_CODES: cancels (1), correctsText (2), correctsAmounts (3).
reasonRazonRef90Reason of the reference.
globalIndGlobalA global reference (IndGlobal=1).
otherRutRUTOtrRUT of another issuer, validated with rut.ts.

A credit note that does not give the right to reduce the VAT debit sets options: { noDebitReduction: 1 } (IndNoRebaja).

References of a receipt#

Receipts have their own schema for Referencia: no date, no global flag and no other-issuer RUT, and CodRef is the company's own free text. That is why a receipt uses receiptReferences instead of references, and the two never mix: references on a receipt, or receiptReferences on any other type, throws INVALID_OPTION with details.field naming the field.

TypeScript
const receipt = createDocument({
  type: 39,
  folio: 5,
  issuer,
  items: [{ name: 'Café', unitPrice: 1500 }],
  pricesIncludeVat: true,
  receiptReferences: [{ code: 'SET', reason: 'CASO-1' }],
})
 
receipt.receiptReferences // [{ lineNumber: 1, code: 'SET', reason: 'CASO-1' }]
FieldSII tagLimitMeaning
codeCodRef18The company's own reference code; SET in the certification test set.
reasonRazonRef90Reason.
documentTypeTpoDocRef3Type of the referenced document (a number or a string, stored as a string).
folioFolioRef18Folio of the referenced document (a number or a string, stored as a string).

All four are optional, and empty texts are dropped.

Global discounts and surcharges#

globalAdjustments are discounts or surcharges over the whole document (DscRcgGlobal):

TypeScript
globalAdjustments: [
  { kind: 'discount', valueType: 'percent', value: 10, description: 'Descuento cliente' },
]
FieldSII tagLimitMeaning
kindTpoMov'discount' (D) or 'surcharge' (R).
valueTypeTpoValor'percent' (%) or 'amount' ($).
valueValorDRThe value, in the unit of valueType.
descriptionGlosaDR45Description.
exemptIndicatorIndExeDR1 applies the adjustment to the exempt amount, 2 to the non-billable one. Omit it for the affected amount.

The totals take them into account; see Totals.

Subtotals#

subtotals is a list of SubtotalInfo (SubTotInfo) that the SDK writes as given, without computing anything: number (NroSTI), totalAmount (ValSubtotSTI) and, optionally, description (GlosaSTI), order (OrdenSTI), netAmount, vatAmount, additionalTaxAmount, exemptAmount and the lines (LineasDeta) they include.

Commissions (settlement invoice 43)#

A settlement invoice (43) is issued by an agent on behalf of a principal. Every line needs settledDocumentType, and commissions carries the commission and other-charge lines (Comisiones):

TypeScript
const settlement = createDocument({
  type: 43,
  folio: 1,
  issuer,
  receiver,
  items: [{ name: 'Venta liquidada', unitPrice: 10000, settledDocumentType: '33' }],
  commissions: [
    { kind: 'commission', description: 'Comisión 10%', rate: 10, netValue: 1000, vatValue: 190 },
  ],
})
FieldSII tagLimitMeaning
kindTipoMovim'commission' (C) or 'otherCharge' (O).
descriptionGlosa60Description (required).
rateTasaComisionCommission rate in percent.
netValueValComNetoNet value.
exemptValueValComExeExempt value.
vatValueValComIVAVAT of the commission.

calculateTotals does not derive the commission totals, own VAT (IVAProp) or third-party VAT (IVATerc) of a settlement invoice. Compute them yourself and pass them in totals (see Explicit totals). The signed element of a settlement invoice is Liquidacion, not Documento.

Transport (dispatch guide 52)#

A dispatch guide (52) says what moves, where and why. Two header options give the reason and who dispatches (options.transferType, options.dispatchType), and transport carries the vehicle, the carrier, the driver and the destination:

TypeScript
import { createDocument, DISPATCH_TYPES, TRANSFER_TYPES } from 'dte-sdk'
 
const guide = createDocument({
  type: 52,
  folio: 1,
  issuer,
  receiver,
  items: [{ name: 'Pallet', quantity: 4, unit: 'UN', unitPrice: 25000 }],
  options: {
    transferType: TRANSFER_TYPES.sale,
    dispatchType: DISPATCH_TYPES.byIssuerToReceiver,
  },
  transport: {
    licensePlate: 'ABCD12',
    carrierRut: '77654321-7',
    driver: { rut: '10000004-0', name: 'Juan Perez' },
    destinationAddress: 'Pasaje Los Olmos 88',
    destinationCommune: 'Providencia',
  },
})
 
guide.header.options // { transferType: 1, dispatchType: 2 }
guide.header.transport.driver // { rut: '10000004-0', name: 'Juan Perez' }
guide.header.totals // { netAmount: 100000, vatRate: 19, vatAmount: 19000, totalAmount: 119000 }
transport fieldSII tagLimitMeaning
licensePlatePatente8Vehicle license plate.
carrierRutRUTTransCarrier RUT, validated with rut.ts.
driver.rutRUTChoferDriver RUT, validated with rut.ts.
driver.nameNombreChofer30Driver name (required when driver is given).
destinationAddressDirDest70Destination address.
destinationCommuneCmnaDest20Destination commune.
destinationCityCiudadDest20Destination city.

TRANSFER_TYPES (IndTraslado): sale 1, pendingSale 2, consignment 3, freeDelivery 4, internalTransfer 5, otherNonSale 6, return 7, exportTransfer 8, exportSale 9. DISPATCH_TYPES (TipoDespacho): byReceiver 1, byIssuerToReceiver 2, byIssuerToOther 3.

A guide for a transfer without a price can leave unitPrice out: its totals are { vatRate: 19, totalAmount: 0 }.

Header options#

options holds the optional IdDoc fields. Only the ones you set are written, in schema order. You never need most of them; the useful ones are paymentTerms, dueDate, payments, transferType and dispatchType.

TypeScript
import { createDocument, PAYMENT_TERMS } from 'dte-sdk'
 
createDocument({
  type: 33,
  folio: 3,
  issuer,
  receiver,
  items: [{ name: 'Plan anual', unitPrice: 100000 }],
  options: {
    paymentTerms: PAYMENT_TERMS.credit,
    dueDate: '2026-12-15',
    payments: [{ date: '2026-12-15', amount: 119000, note: 'Pago único' }],
  },
})
OptionSII tagMeaning
noDebitReductionIndNoRebajaCredit note that does not reduce the debit (1).
dispatchTypeTipoDespachoDISPATCH_TYPES (1 to 3).
transferTypeIndTrasladoTRANSFER_TYPES (1 to 9).
printFormatTpoImpresion'N' normal or 'T' ticket.
serviceIndicatorIndServicioSERVICE_INDICATORS: 1 periodic services, 2 periodic home services, 3 sales and services, 4 shows. Defaults to 3 on receipts.
grossAmountsMntBrutoLine amounts include VAT (1). Set for you by pricesIncludeVat.
purchaseTransactionTypeTpoTranCompraPurchase transaction type (1 to 7).
saleTransactionTypeTpoTranVentaSale transaction type (1 to 4).
paymentTermsFmaPagoPAYMENT_TERMS: cash 1, credit 2, free 3.
exportPaymentTermsFmaPagExpExport payment terms.
settlementDateFchCancelSettlement date.
settledAmountMntCancelSettled amount.
outstandingBalanceSaldoInsolOutstanding balance.
paymentsMntPagosInstallments: { date, amount, note? } (FchPago, MntPago, GlosaPagos).
billingPeriodPeriodoDesde, PeriodoHasta{ from, to }.
paymentMediumMedioPagoPayment medium.
paymentAccountTypeTpoCtaPagoPayment account type.
paymentAccountNumberNumCtaPagoPayment account number.
paymentBankBcoPagoPayment bank.
paymentTermsCodeTermPagoCdgPayment terms code.
paymentTermsNoteTermPagoGlosaPayment terms note.
paymentTermsDaysTermPagoDiasPayment terms days.
dueDateFchVencDue date.
netAmountsIndicatorIndMntNetoNet amounts indicator.

The SDK writes the options as you give them, without validating or measuring them. Dates in options are not normalized: write them as YYYY-MM-DD.

VAT-included prices#

When your prices already include VAT, say so with pricesIncludeVat: true. The SDK then derives net and VAT from the gross amount and, on every type but receipts, writes MntBruto=1 (options.grossAmounts). Receipts do not carry MntBruto, but the totals are derived the same way:

TypeScript
const receipt = createDocument({
  type: 39,
  folio: 1,
  issuer,
  items: [{ name: 'Café', quantity: 2, unitPrice: 1500 }],
  pricesIncludeVat: true,
})
 
receipt.header.receiver // { rut: '66666666-6', businessName: 'Consumidor Final', address: 'Sin Direccion', commune: 'Santiago' }
receipt.header.options // { serviceIndicator: 3 }
receipt.header.totals // { netAmount: 2521, vatAmount: 479, totalAmount: 3000 }

The SDK sets grossAmounts automatically only when the document has an affected amount: an exempt-only invoice with pricesIncludeVat gets no MntBruto.

Withholding (purchase invoice 46)#

A purchase invoice (46) is issued by the buyer, who withholds the VAT instead of paying it to the seller. The issuer is the buyer; the receiver is the seller. VAT is withheld in full by default:

TypeScript
const purchase = createDocument({
  type: 46,
  folio: 1,
  issuer,
  receiver: {
    rut: '77654321-7',
    businessName: 'Distribuidora Maipo Ltda',
    businessActivity: 'Reciclaje',
    address: 'Camino Lo Boza 410',
    commune: 'Pudahuel',
  },
  items: [{ name: 'Chatarra', quantity: 10, unitPrice: 1000 }],
})
 
purchase.header.totals
// {
//   netAmount: 10000,
//   vatRate: 19,
//   vatAmount: 1900,
//   withholdings: [{ taxCode: 15, rate: 19, amount: 1900 }],
//   totalAmount: 10000,
// }

totalAmount leaves out the withheld VAT. Pass withholdVat: false to issue a 46 without withholding, and withholdVat: true on a debit or credit note that affects a purchase invoice, so the note withholds the VAT the same way. On any other type withholdVat defaults to false.

Explicit totals#

totals replaces the computed totals with the object you give (a Totals). Use it when you need amounts the SDK does not derive (commission totals of a settlement invoice, IVAProp, IVATerc, MontoPeriodo, SaldoAnterior, VlrPagar) or a VAT rate other than 19. The SDK trusts it: it does not check the object against the lines. See Totals for the fields.

What validation does#

createDocument fails early instead of letting the SII reject the whole envelope later (the SII rejects an envelope when one of its documents fails the schema). Among other things, it:

  1. Checks the type (INVALID_DOCUMENT_TYPE) and the folio, a positive integer (INVALID_FOLIO).
  2. Takes the issue date as given or today in Chile, and requires it to be a real date (INVALID_DATE).
  3. Normalizes every RUT with rut.ts (INVALID_RUT, with details.field such as issuer.rut or references[0].otherRut). The check digit is validated; the value is never copied into the error message.
  4. Sanitizes free text: removes apostrophes and double quotes (they break the SII signature checks), turns typographic dashes and ellipsis into ASCII, drops everything outside ISO-8859-1 (emoji, curly quotes) and trims. "Premium" — grande becomes Premium - grande.
  5. Enforces the schema lengths of the sanitized text (FIELD_TOO_LONG).
  6. Requires what the type needs: the receiver (MISSING_FIELD, details.field: 'receiver'), its address and commune on every type but receipts, at least one item, an item name, the issuer business name, activity, address and commune, and settledDocumentType on every line of a 43.
  7. Rejects non-finite numbers in amounts (INVALID_AMOUNT) and a unitPriceDecimals outside 0 to 6 (INVALID_OPTION).
  8. Numbers items, references, adjustments and commissions from 1.
  9. Computes line amounts and totals (see Totals), applies the receipt defaults and sets MntBruto.
CodeWhen
INVALID_DOCUMENT_TYPEtype is not one of the nine supported codes.
INVALID_FOLIOfolio is not a positive integer.
INVALID_DATEA date (issueDate, a reference date) is not a real calendar date.
INVALID_RUTA RUT fails rut.ts. details.field says which.
MISSING_FIELDA required field is empty or absent. details.field is its path, such as items[0].name.
FIELD_TOO_LONGA sanitized text exceeds its limit. details has field (the SII tag), maxLength, length and value.
INVALID_AMOUNTA quantity, price, percent or amount is not a finite number.
INVALID_OPTIONreferences on a receipt, receiptReferences on another type, or a bad unitPriceDecimals.
TypeScript
import { createDocument, isDteError } from 'dte-sdk'
 
try {
  createDocument({ ...input, items: [{ name: 'x'.repeat(81), unitPrice: 1 }] })
} catch (error) {
  if (isDteError(error) && error.code === 'FIELD_TOO_LONG') {
    error.message // 'Field exceeds the maximum length allowed by the SII schema (field=NmbItem, maxLength=80, length=81)'
    error.details // { field: 'NmbItem', maxLength: 80, length: 81, value: 'xxxxxxxx…' }
  }
}

Messages never include RUTs, names or item text; the offending value lives in details. If you log details, keep in mind that it can hold personal data. See Errors.

Field limits#

FIELD_LIMITS is the table of xs:maxLength values that createDocument enforces, keyed by SII tag:

TagMaxTagMax
RznSoc100NmbItem80
GiroEmis80DscItem1000
DirOrigen70UnmdItem, UnmdRef4
CmnaOrigen, CiudadOrigen, Sucursal20TpoDocLiq3
CdgVendedor60RazonRef90
RznSocRecep100CodRef, FolioRef18
GiroRecep40TpoDocRef3
Contacto80GlosaDR45
DirRecep, DirPostal70Glosa60
CmnaRecep, CiudadRecep20Patente8
CmnaPostal, CiudadPostal20NombreChofer30
CdgIntRecep20DirDest70
CmnaDest, CiudadDest20

FIELD_LIMITS also lists CorreoRecep (80) and GlosaSTI (40), but createDocument does not check the receiver email or the subtotal description today. Fields that are not free text (phone, email, activityCode, codes, options, subtotals) are written as you give them.

Issue date and the clock#

issueDate accepts YYYY-MM-DD, DD-MM-YYYY and DD/MM/YYYY (the way the SII portal and resolutions show dates) and is stored as YYYY-MM-DD. When you omit it, the SDK uses today's date in Chile (America/Santiago, with daylight saving), never the time zone of the process. That matters on a server in UTC: at 23:30 in Chile on 15 November the UTC date is already the 16th.

Every function that depends on the time takes the clock as an option, so tests are deterministic:

TypeScript
const document = createDocument(input, { now: new Date('2026-11-15T18:30:00Z') })
document.header.issueDate // '2026-11-15'

issueDocument takes the same now and passes it to the stamp and the signature, so one now fixes FchEmis, TSTED and TmstFirma together. timestamp overrides the stamp and signature times with a string you choose (YYYY-MM-DDTHH:MM:SS).

Render the XML#

buildDocumentXml(document, options?) renders the unsigned <DTE> XML:

TypeScript
import { buildDocumentXml, documentElementName } from 'dte-sdk'
 
const xml = buildDocumentXml(document)
documentElementName(33) // 'Documento'
documentElementName(43) // 'Liquidacion'
OptionMeaning
tedThe Ted to embed (verbatim, from stampDocument).
signedAtValue of TmstFirma.

What it does:

  • Declares the namespaces on the DTE root (http://www.sii.cl/SiiDte and xsi), so the signature digest inherits them.
  • Puts the ID attribute (DTE_T33F1) on the signed element: Documento, or Liquidacion for a settlement invoice (43). documentElementName(type) returns that name.
  • Writes the sections in schema order (Encabezado, Detalle, SubTotInfo, DscRcgGlobal, Referencia, Comisiones, TED) and leaves out the empty ones.
  • Puts a line break after each of those sections. The SII rejects any line longer than about 4,090 characters (CHR-00002), so a receipt with many lines would fail otherwise. The breaks sit inside the signed element and the digest covers them.
  • Escapes text values as XML entities.

You rarely call it yourself: signDocument calls it for you. It is useful to look at what would be signed, or to archive a draft.

The codec: SII tags and the English model#

The English model and the SII tags are two shapes of the same data. toSiiDocument maps a Document to the structure with the official tags in schema order (SiiDocumento: Encabezado.IdDoc.TipoDTE, Totales.MntTotal…), and fromSiiDocument maps it back:

TypeScript
import { fromSiiDocument, toSiiDocument } from 'dte-sdk'
 
const sii = toSiiDocument(document)
sii.Encabezado.Totales.MntTotal // 167195
sii.Detalle[0].NmbItem // 'Plan anual'
 
fromSiiDocument(sii) // a Document again

toSiiDocument is the only place that knows the tag names; buildDocumentXml always renders from its output. fromSiiDocument is for DTE you receive and for interoperability: it validates every RUT with rut.ts on the way in (INVALID_RUT). A receipt maps its references to and from receiptReferences. One thing does not survive the round trip: unitPriceDecimals, because a written 12.50 reads back as the number 12.5.

Next steps#