Saltar al contenido
dte-sdk

Documentos

Un documento parte como datos planos que describes en inglés legible (issuer.businessName, items[0].unitPrice) y termina como el XML que espera el SII. createDocument es el primer paso: valida lo que le entregas, normaliza RUT y textos, numera las líneas, calcula los totales y devuelve un Document. issueDocument ejecuta todo el flujo (crear, timbrar, firmar) en una sola llamada. buildDocumentXml y el codec (toSiiDocument, fromSiiDocument) convierten el modelo a etiquetas del SII y de vuelta.

Todo lo de esta página vive en la entrada dte-sdk y corre en Node, Bun y Deno.

Los documentos, timbres, firmas y sobres están probados contra datos sintéticos y contra los esquemas oficiales, pero todavía no se han enviado al SII real. Mira Estado para saber qué ha pasado por el SII real y qué no.

Crear un documento#

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 es pura: la misma entrada da la misma salida, nunca modifica su argumento y no toca red, archivos ni secretos. Aquí el folio no se compara con ningún CAF; eso ocurre al timbrar el documento (mira Timbre).

Emitir en una sola llamada#

issueDocument recibe un DocumentInput (o un Document que ya creaste), un CAF parseado y un certificado, y devuelve un documento firmado:

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 // <DTE> firmado con su TED, sin declaración XML
signed.ted // el timbre electrónico
signed.signedAt // hora de Chile de la firma
OpciónSignificado
documentUn DocumentInput (pasa por createDocument) o un Document ya creado.
cafEl CAF parseado que cubre el tipo, el emisor y el folio.
certificateEl certificado del emisor que firma el documento.
nowReloj para la fecha de emisión, TSTED y TmstFirma. Los tests lo usan para tener una salida determinista.
checkCafExpiryChequear el vencimiento del CAF (por defecto true). Mira Timbre.
timestampTSTED y TmstFirma exactamente como se entregan, en vez de la hora de Chile de now.

El resultado es un SignedDocument: un Document más ted, xml, signedAt y signature. Los pasos que ejecuta son los documentados en Timbre y Firma; llámalos por separado cuando necesites detenerte a la mitad (para inspeccionar el TED, por ejemplo).

Tipos de documento#

El SDK soporta nueve códigos TipoDTE. DOCUMENT_TYPES los nombra en inglés, DOCUMENT_TYPE_VALUES los lista en orden ascendente e isDocumentType(value) es una guarda de tipo.

CódigoClave de DOCUMENT_TYPESNombre oficial (documentTypeName)BoletaExentoNotaExige receptorTransferible
33invoiceFactura Electrónicanononosísí
34exemptInvoiceFactura Exenta Electrónicanosínosísí
39receiptBoleta Electrónicasínononono
41exemptReceiptBoleta Exenta Electrónicasísínonono
43settlementInvoiceLiquidación Factura Electrónicanononosísí
46purchaseInvoiceFactura de Compra Electrónicanononosísí
52dispatchGuideGuía de Despacho Electrónicanononosísí
56debitNoteNota de Débito Electrónicanonosísíno
61creditNoteNota de Crédito Electrónicanonosísíno

Las columnas son las respuestas de los helpers del mismo nombre:

Helpertrue paraQué significa
isReceipt(type)39, 41Boletas: no exigen receptor identificado, no llevan TasaIVA, tienen su propia forma de referencia y se envían por REST.
isExemptType(type)34, 41Todas las líneas son exentas.
isNote(type)56, 61Notas de débito y de crédito, que el SII espera que referencien otro documento.
requiresReceiver(type)todo salvo 39 y 41El tipo exige un receptor identificado.
isTransferable(type)33, 34, 43, 46, 52El documento impreso lleva copia cedible y el recuadro de acuse de recibo de la Ley 19.983.

documentTypeName(type, variant) devuelve el nombre oficial en español. La variante es 'long' (por defecto, como en la tabla), 'short' (mayúsculas sin tildes: NOTA CREDITO) o 'printed' (mayúsculas, como lo escribe el manual de muestras impresas del SII: NOTA DE CRÉDITO ELECTRÓNICA).

createDocument no obliga a que una nota lleve una referencia. El SII espera que toda nota de débito o de crédito referencie el documento que afecta, así que agrega references tú mismo (mira Notas de crédito y de débito).

El modelo DocumentInput#

DocumentInput es lo que le entregas a createDocument. Los campos obligatorios van en negrita en la primera tabla; todo lo demás es opcional. Los campos nombrados con etiquetas del SII en las tablas siguientes son las etiquetas que escribe el codec (TipoDTE, MntTotal…), así puedes seguir un campo desde tu entrada hasta el XML.

CampoTipoSignificado
typeDocumentTypeTipoDTE: uno de 33, 34, 39, 41, 43, 46, 52, 56, 61.
folionumberFolio: un entero positivo autorizado por un CAF.
issueDatestringFchEmis, AAAA-MM-DD (también se aceptan DD-MM-AAAA y DD/MM/AAAA). Por defecto, hoy en Chile.
issuerIssuerInputQuién emite el documento.
receiverReceiverInputQuién lo recibe. Obligatorio en todos los tipos salvo 39 y 41.
itemsLineItemInput[]Las líneas de detalle. Al menos una.
referencesReferenceInput[]Referencias a otros documentos (todos los tipos salvo boletas).
receiptReferencesReceiptReferenceInput[]Referencias de una boleta (solo 39 y 41).
globalAdjustmentsGlobalAdjustmentInput[]Descuentos y recargos globales (DscRcgGlobal).
subtotalsSubtotalInfo[]Subtotales informativos (SubTotInfo), escritos tal como vienen.
commissionsCommissionLineInput[]Líneas de comisión de una liquidación factura (43).
transportTransportInputDatos de transporte, para guías de despacho (52).
optionsHeaderOptionsCampos opcionales de IdDoc: forma de pago, vencimiento, tipos de despacho y de traslado…
pricesIncludeVatbooleanLos precios unitarios ya incluyen IVA (MntBruto=1).
withholdVatbooleanRetener todo el IVA (ImptoReten 15). true por defecto en facturas de compra (46).
totalsTotalsTotales explícitos (avanzado). Se calculan desde las líneas si se omiten.

Emisor#

El emisor es la empresa que emite el documento (Emisor). El RUT puede venir en cualquier forma (76.543.210-3, 76543210-3, 765432103); rut.ts lo valida y el documento lo guarda como 76543210-3.

CampoObligatorioEtiqueta SIILímiteNotas
rutsíRUTEmisorValidado con rut.ts.
businessNamesíRznSoc (RznSocEmisor en boletas)100Razón social.
businessActivitysíGiroEmis (GiroEmisor en boletas)80Giro comercial.
addresssíDirOrigen70
communesíCmnaOrigen20
citynoCiudadOrigen20Por defecto, la comuna.
activityCodenoActecoCódigo de actividad económica del SII (un número).
phonenoTelefonoNo se escribe en boletas.
emailnoCorreoEmisorNo se escribe en boletas.
branchnoSucursal20Nombre de la sucursal.
siiBranchCodenoCdgSIISucurCódigo de sucursal inscrito en el SII.
salespersonCodenoCdgVendedor60No se escribe en boletas.

Receptor#

CampoObligatorioEtiqueta SIILímiteNotas
rutsí (si se entrega un receptor)RUTRecepValidado con rut.ts.
businessNamesí (si se entrega un receptor)RznSocRecep100
addresssí, salvo en boletasDirRecep70
communesí, salvo en boletasCmnaRecep20
businessActivitynoGiroRecep40No se escribe en boletas.
internalCodenoCdgIntRecep20
contactnoContacto80
emailnoCorreoRecepNo se escribe en boletas.
citynoCiudadRecep20
postalAddressnoDirPostal70No se escribe en boletas.
postalCommunenoCmnaPostal20No se escribe en boletas.
postalCitynoCiudadPostal20No se escribe en boletas.

En una boleta (39, 41) puedes omitir receiver. El documento lleva entonces al consumidor final genérico: RUT 66666666-6, nombre Consumidor Final, dirección Sin Direccion, comuna Santiago. Un receptor que sí entregues en una boleta se conserva tal cual, solo con lo que escribiste (su nombre sigue siendo obligatorio).

Ítems#

Cada elemento de items es un LineItemInput. El SDK numera las líneas desde 1 (NroLinDet) y calcula amount (MontoItem) salvo que lo entregues.

CampoEtiqueta SIILímiteSignificado
name (obligatorio)NmbItem80Nombre del ítem.
descriptionDscItem1000Descripción.
quantityQtyItemCantidad. Cuenta como 1 en el monto cuando se omite, pero no se escribe.
unitUnmdItem4Unidad de medida.
unitPricePrcItemPrecio unitario, neto o con IVA según pricesIncludeVat. Cuenta como 0 cuando se omite.
unitPriceDecimalsPrcItemDecimales con que se escribe el precio, un entero de 0 a 6. Sin él, el número se escribe tal cual.
discountPercentDescuentoPctDescuento de la línea en porcentaje. El SDK también escribe DescuentoMonto.
surchargePercentRecargoPctRecargo de la línea en porcentaje. El SDK también escribe RecargoMonto.
exemptIndExeAtajo para exemptIndicator: 1.
exemptIndicatorIndExe1 exento, 2 no facturable, 3 garantía por depósito de envases, 4 no constituye venta, 5 ítem a rebajar, 6 no facturable negativo (EXEMPT_INDICATORS).
codesCdgItemHasta 5 códigos: { type: 'INT1', value: 'T-100' }.
additionalTaxCodesCodImpAdicHasta 2 códigos de impuesto adicional (números).
settledDocumentTypeTpoDocLiq3Tipo del documento liquidado. Obligatorio en cada línea de una liquidación factura (43).
referenceQuantityQtyRefCantidad de referencia.
referenceUnitUnmdRef4Unidad de referencia.
referencePricePrcRefPrecio de referencia.
amountMontoItemMonto explícito de la línea. Omite el cálculo cantidad por precio.

Cómo se calcula el monto de una línea (redondeo por línea, descuentos, recargos) está en Totales.

Un ítem es exento cuando pones exempt: true, fijas un exemptIndicator, o cuando el tipo de documento mismo es exento (34 y 41 dan IndExe=1 a cada línea salvo que fijes otro indicador). El SDK no revisa el largo de codes ni de additionalTaxCodes: se escriben como los entregas.

Los precios con decimales necesitan unitPriceDecimals, porque el monto siempre es en pesos enteros pero el SII lee PrcItem como está escrito:

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

Notas de crédito y de débito con referencias#

references vincula el documento con otros. En las notas de crédito y de débito (61, 56) es la forma de decir qué documento anulas o corriges.

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 }
CampoEtiqueta SIILímiteSignificado
documentTypeTpoDocRefTipo del documento referenciado (un número), o el string 'SET' en los sets de pruebas de certificación.
folioFolioRefFolio del documento referenciado (número o string; se guarda como string).
dateFchRefFecha del documento referenciado, AAAA-MM-DD (o DD-MM-AAAA).
codeCodRefREFERENCE_CODES: cancels (1, anula), correctsText (2, corrige texto), correctsAmounts (3, corrige montos).
reasonRazonRef90Razón de la referencia.
globalIndGlobalUna referencia global (IndGlobal=1).
otherRutRUTOtrRUT de otro contribuyente, validado con rut.ts.

Una nota de crédito que no da derecho a descontar débito fiscal de IVA fija options: { noDebitReduction: 1 } (IndNoRebaja).

Referencias de una boleta#

Las boletas tienen su propio esquema para Referencia: sin fecha, sin indicador global y sin RUT de otro contribuyente, y CodRef es texto libre propio de la empresa. Por eso una boleta usa receiptReferences en vez de references, y las dos nunca se mezclan: references en una boleta, o receiptReferences en cualquier otro tipo, lanza INVALID_OPTION con details.field indicando el campo.

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' }]
CampoEtiqueta SIILímiteSignificado
codeCodRef18El código de referencia propio de la empresa; SET en el set de pruebas de certificación.
reasonRazonRef90Razón.
documentTypeTpoDocRef3Tipo del documento referenciado (número o string, se guarda como string).
folioFolioRef18Folio del documento referenciado (número o string, se guarda como string).

Los cuatro son opcionales, y los textos vacíos se descartan.

Descuentos y recargos globales#

globalAdjustments son descuentos o recargos sobre todo el documento (DscRcgGlobal):

TypeScript
globalAdjustments: [
  { kind: 'discount', valueType: 'percent', value: 10, description: 'Descuento cliente' },
]
CampoEtiqueta SIILímiteSignificado
kindTpoMov'discount' (D, descuento) o 'surcharge' (R, recargo).
valueTypeTpoValor'percent' (%) o 'amount' ($).
valueValorDREl valor, en la unidad de valueType.
descriptionGlosaDR45Glosa.
exemptIndicatorIndExeDR1 aplica el ajuste al monto exento, 2 al no facturable. Omítelo para el monto afecto.

Los totales los consideran; mira Totales.

Subtotales#

subtotals es una lista de SubtotalInfo (SubTotInfo) que el SDK escribe tal como viene, sin calcular nada: number (NroSTI), totalAmount (ValSubtotSTI) y, opcionalmente, description (GlosaSTI), order (OrdenSTI), netAmount, vatAmount, additionalTaxAmount, exemptAmount y las lines (LineasDeta) que incluye.

Comisiones (liquidación factura 43)#

Una liquidación factura (43) la emite un agente por cuenta de un mandante. Cada línea necesita settledDocumentType, y commissions lleva las líneas de comisión y otros cargos (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 },
  ],
})
CampoEtiqueta SIILímiteSignificado
kindTipoMovim'commission' (C, comisión) u 'otherCharge' (O, otro cargo).
descriptionGlosa60Glosa (obligatoria).
rateTasaComisionTasa de la comisión en porcentaje.
netValueValComNetoValor neto.
exemptValueValComExeValor exento.
vatValueValComIVAIVA de la comisión.

calculateTotals no deriva los totales de comisiones, el IVA propio (IVAProp) ni el IVA de terceros (IVATerc) de una liquidación factura. Calcúlalos tú y entrégalos en totals (mira Totales explícitos). El elemento firmado de una liquidación factura es Liquidacion, no Documento.

Transporte (guía de despacho 52)#

Una guía de despacho (52) dice qué se mueve, hacia dónde y por qué. Dos opciones del encabezado dan el motivo y quién despacha (options.transferType, options.dispatchType), y transport lleva el vehículo, el transportista, el chofer y el destino:

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 }
Campo de transportEtiqueta SIILímiteSignificado
licensePlatePatente8Patente del vehículo.
carrierRutRUTTransRUT del transportista, validado con rut.ts.
driver.rutRUTChoferRUT del chofer, validado con rut.ts.
driver.nameNombreChofer30Nombre del chofer (obligatorio cuando se entrega driver).
destinationAddressDirDest70Dirección de destino.
destinationCommuneCmnaDest20Comuna de destino.
destinationCityCiudadDest20Ciudad de destino.

TRANSFER_TYPES (IndTraslado): sale 1 (venta), pendingSale 2 (ventas por efectuar), consignment 3 (consignación), freeDelivery 4 (entrega gratuita), internalTransfer 5 (traslado interno), otherNonSale 6 (otros no venta), return 7 (devolución), exportTransfer 8 (traslado para exportación), exportSale 9 (venta para exportación). DISPATCH_TYPES (TipoDespacho): byReceiver 1 (por cuenta del receptor), byIssuerToReceiver 2 (por cuenta del emisor a instalaciones del receptor), byIssuerToOther 3 (por cuenta del emisor a otras instalaciones).

Una guía de un traslado sin precio puede omitir unitPrice: sus totales son { vatRate: 19, totalAmount: 0 }.

Opciones del encabezado#

options contiene los campos opcionales de IdDoc. Solo se escriben los que fijas, en el orden del esquema. La mayoría no las necesitarás nunca; las útiles son paymentTerms, dueDate, payments, transferType y 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' }],
  },
})
OpciónEtiqueta SIISignificado
noDebitReductionIndNoRebajaNota de crédito sin derecho a descontar débito (1).
dispatchTypeTipoDespachoDISPATCH_TYPES (1 a 3).
transferTypeIndTrasladoTRANSFER_TYPES (1 a 9).
printFormatTpoImpresion'N' normal o 'T' ticket.
serviceIndicatorIndServicioSERVICE_INDICATORS: 1 servicios periódicos, 2 servicios periódicos domiciliarios, 3 ventas y servicios, 4 espectáculos. Por defecto 3 en boletas.
grossAmountsMntBrutoLos montos de las líneas incluyen IVA (1). Lo fija pricesIncludeVat por ti.
purchaseTransactionTypeTpoTranCompraTipo de transacción de compra (1 a 7).
saleTransactionTypeTpoTranVentaTipo de transacción de venta (1 a 4).
paymentTermsFmaPagoPAYMENT_TERMS: cash 1 (contado), credit 2 (crédito), free 3 (sin costo).
exportPaymentTermsFmaPagExpForma de pago de exportación.
settlementDateFchCancelFecha de cancelación.
settledAmountMntCancelMonto cancelado.
outstandingBalanceSaldoInsolSaldo insoluto.
paymentsMntPagosCuotas: { date, amount, note? } (FchPago, MntPago, GlosaPagos).
billingPeriodPeriodoDesde, PeriodoHasta{ from, to }.
paymentMediumMedioPagoMedio de pago.
paymentAccountTypeTpoCtaPagoTipo de cuenta de pago.
paymentAccountNumberNumCtaPagoNúmero de cuenta de pago.
paymentBankBcoPagoBanco de pago.
paymentTermsCodeTermPagoCdgCódigo de términos de pago.
paymentTermsNoteTermPagoGlosaGlosa de términos de pago.
paymentTermsDaysTermPagoDiasDías de los términos de pago.
dueDateFchVencFecha de vencimiento.
netAmountsIndicatorIndMntNetoIndicador de montos netos.

El SDK escribe las opciones como las entregas, sin validarlas ni medirlas. Las fechas dentro de options no se normalizan: escríbelas como AAAA-MM-DD.

Precios con IVA incluido#

Cuando tus precios ya incluyen IVA, indícalo con pricesIncludeVat: true. El SDK deriva entonces el neto y el IVA desde el monto bruto y, en todos los tipos salvo boletas, escribe MntBruto=1 (options.grossAmounts). Las boletas no llevan MntBruto, pero los totales se derivan igual:

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 }

El SDK fija grossAmounts automáticamente solo cuando el documento tiene monto afecto: una factura solo con líneas exentas y pricesIncludeVat no lleva MntBruto.

Retención (factura de compra 46)#

Una factura de compra (46) la emite el comprador, que retiene el IVA en vez de pagárselo al vendedor. El emisor es el comprador; el receptor es el vendedor. Por defecto se retiene todo el IVA:

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 deja fuera el IVA retenido. Pasa withholdVat: false para emitir una 46 sin retención, y withholdVat: true en una nota de débito o de crédito que afecta una factura de compra, para que la nota retenga el IVA de la misma forma. En cualquier otro tipo withholdVat es false por defecto.

Totales explícitos#

totals reemplaza los totales calculados por el objeto que entregues (un Totals). Úsalo cuando necesites montos que el SDK no deriva (totales de comisiones de una liquidación factura, IVAProp, IVATerc, MontoPeriodo, SaldoAnterior, VlrPagar) o una tasa de IVA distinta de 19. El SDK confía en él: no lo contrasta con las líneas. Mira Totales para los campos.

Qué valida#

createDocument falla temprano en vez de dejar que el SII rechace después el sobre completo (el SII rechaza un sobre cuando uno de sus documentos falla el esquema). Entre otras cosas:

  1. Revisa el tipo (INVALID_DOCUMENT_TYPE) y el folio, un entero positivo (INVALID_FOLIO).
  2. Toma la fecha de emisión como viene o la de hoy en Chile, y exige que sea una fecha real (INVALID_DATE).
  3. Normaliza cada RUT con rut.ts (INVALID_RUT, con details.field como issuer.rut o references[0].otherRut). Se valida el dígito verificador; el valor nunca se copia al mensaje del error.
  4. Sanitiza los textos libres: quita apóstrofes y comillas dobles (rompen las validaciones de firma del SII), convierte guiones y puntos suspensivos tipográficos a ASCII, elimina todo lo que está fuera de ISO-8859-1 (emoji, comillas curvas) y recorta espacios. "Premium" — grande queda como Premium - grande.
  5. Exige los largos del esquema sobre el texto ya sanitizado (FIELD_TOO_LONG).
  6. Exige lo que el tipo necesita: el receptor (MISSING_FIELD, details.field: 'receiver'), su dirección y comuna en todos los tipos salvo boletas, al menos un ítem, el nombre del ítem, la razón social, el giro, la dirección y la comuna del emisor, y settledDocumentType en cada línea de una 43.
  7. Rechaza números no finitos en los montos (INVALID_AMOUNT) y un unitPriceDecimals fuera de 0 a 6 (INVALID_OPTION).
  8. Numera ítems, referencias, ajustes y comisiones desde 1.
  9. Calcula los montos de línea y los totales (mira Totales), aplica los defaults de boleta y fija MntBruto.
CódigoCuándo
INVALID_DOCUMENT_TYPEtype no es uno de los nueve códigos soportados.
INVALID_FOLIOfolio no es un entero positivo.
INVALID_DATEUna fecha (issueDate, la date de una referencia) no es una fecha de calendario real.
INVALID_RUTUn RUT no pasa rut.ts. details.field dice cuál.
MISSING_FIELDFalta un campo obligatorio o está vacío. details.field es su ruta, como items[0].name.
FIELD_TOO_LONGUn texto sanitizado supera su límite. details trae field (la etiqueta del SII), maxLength, length y value.
INVALID_AMOUNTUna cantidad, precio, porcentaje o monto no es un número finito.
INVALID_OPTIONreferences en una boleta, receiptReferences en otro tipo, o un unitPriceDecimals inválido.
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…' }
  }
}

Los mensajes nunca incluyen RUT, nombres ni texto de ítems; el valor ofensivo vive en details. Si registras details en un log, ten presente que puede contener datos personales. Mira Errores.

Límites de campos#

FIELD_LIMITS es la tabla de valores xs:maxLength que createDocument exige, por etiqueta del SII:

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

FIELD_LIMITS también lista CorreoRecep (80) y GlosaSTI (40), pero hoy createDocument no revisa el correo del receptor ni la glosa de los subtotales. Los campos que no son texto libre (teléfono, correo, activityCode, codes, options, subtotals) se escriben como los entregas.

Fecha de emisión y reloj#

issueDate acepta AAAA-MM-DD, DD-MM-AAAA y DD/MM/AAAA (como muestran las fechas el portal y las resoluciones del SII) y se guarda como AAAA-MM-DD. Cuando la omites, el SDK usa la fecha de hoy en Chile (America/Santiago, con horario de verano), nunca la zona horaria del proceso. Importa en un servidor en UTC: a las 23:30 en Chile del 15 de noviembre, la fecha UTC ya es el 16.

Toda función que depende de la hora recibe el reloj como opción, así los tests son deterministas:

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

issueDocument recibe el mismo now y se lo pasa al timbre y a la firma, así un solo now fija FchEmis, TSTED y TmstFirma a la vez. timestamp reemplaza las horas del timbre y de la firma por un string que tú elijas (AAAA-MM-DDTHH:MM:SS).

Renderizar el XML#

buildDocumentXml(document, options?) renderiza el XML <DTE> sin firma:

TypeScript
import { buildDocumentXml, documentElementName } from 'dte-sdk'
 
const xml = buildDocumentXml(document)
documentElementName(33) // 'Documento'
documentElementName(43) // 'Liquidacion'
OpciónSignificado
tedEl Ted a incluir (textual, desde stampDocument).
signedAtValor de TmstFirma.

Lo que hace:

  • Declara los namespaces en el raíz DTE (http://www.sii.cl/SiiDte y xsi), así el digest de la firma los hereda.
  • Pone el atributo ID (DTE_T33F1) en el elemento firmado: Documento, o Liquidacion en una liquidación factura (43). documentElementName(type) devuelve ese nombre.
  • Escribe las secciones en el orden del esquema (Encabezado, Detalle, SubTotInfo, DscRcgGlobal, Referencia, Comisiones, TED) y omite las vacías.
  • Pone un salto de línea después de cada una de esas secciones. El SII rechaza cualquier línea de más de unos 4.090 caracteres (CHR-00002), así que una boleta con muchas líneas fallaría de otro modo. Los saltos quedan dentro del elemento firmado y el digest los cubre.
  • Escapa los valores de texto como entidades XML.

Rara vez lo llamas tú: signDocument lo llama por ti. Sirve para mirar lo que se va a firmar o para archivar un borrador.

El codec: etiquetas del SII y el modelo en inglés#

El modelo en inglés y las etiquetas del SII son dos formas de los mismos datos. toSiiDocument convierte un Document a la estructura con las etiquetas oficiales en orden de esquema (SiiDocumento: Encabezado.IdDoc.TipoDTE, Totales.MntTotal…), y fromSiiDocument la convierte de vuelta:

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

toSiiDocument es el único lugar que conoce los nombres de las etiquetas; buildDocumentXml siempre renderiza desde su salida. fromSiiDocument es para los DTE que recibes y para interoperar: valida cada RUT con rut.ts al entrar (INVALID_RUT). Una boleta convierte sus referencias hacia y desde receiptReferences. Una cosa no sobrevive al viaje de ida y vuelta: unitPriceDecimals, porque un 12.50 escrito se lee de vuelta como el número 12.5.

Siguientes pasos#