---
title: "Paso 1: Construir el DTE (obligatorio)"
description: "Cómo armar los datos del documento y transformarlos en un DTE timbrado y firmado con la operación builder/build, y qué guardar de la respuesta."
type: "academy"
category: "lesson"
tags: []
authors: [Anonymous]
date: "2026-11-05"
last_update: "2026-11-05"
time_minutes: 9
draft: false
unlisted: false
url: "https://www.apigateway.cl/academy/emision-de-dte/emision/paso-1-construir-el-dte"
---

# Paso 1: Construir el DTE (obligatorio)

Todo empieza con los datos de la venta. Tu sistema los arma en JSON y la operación **`builder/build`** los transforma en el XML oficial del documento. Es la misma operación para todos los documentos: facturas, notas, guías y boletas. En una sola llamada la API:

1. **Normaliza** los datos: completa campos por defecto, numera las líneas y calcula los totales.
2. **Timbra** el documento con la llave del CAF (genera el timbre electrónico, TED).
3. **Firma** el XML con tu certificado digital.

## Los datos del documento

El JSON sigue **la misma estructura y los mismos nombres de campos que el XML oficial del SII**. Por eso los nombres están en español abreviado (`RznSoc`, `NmbItem`, `PrcItem`): son los del SII.

```json
{
    "Encabezado": {
        "IdDoc":    { "...": "qué documento es" },
        "Emisor":   { "...": "quién vende" },
        "Receptor": { "...": "quién compra" },
        "Totales":  { "...": "montos: no los envíes, los calcula la API" }
    },
    "Detalle":      [ { "...": "un objeto por producto o servicio" } ],
    "DscRcgGlobal": [ { "...": "descuentos o recargos globales (opcional)" } ],
    "Referencia":   [ { "...": "documentos relacionados (opcional)" } ]
}
```

No envíes los totales: la API los calcula. Antes de construir, reserva en tu sistema el folio que vas a usar, para que nunca se repita.

El SII define muchos más campos: formas de pago, descuentos, referencias, datos de transporte, impuestos adicionales y otros. La descripción de cada uno, su largo máximo y en qué documentos es obligatorio están en la documentación oficial del SII:

**Documentos tributarios electrónicos (no boletas):**

- [Descripción del formato de DTE](https://www.sii.cl/factura_electronica/factura_mercado/formato_dte_202602.pdf)
- [Diagrama del XML de DTE](https://www.sii.cl/factura_electronica/factura_mercado/diagrama_dte.zip)

**Boletas:**

- [Descripción del formato de boletas](https://www.sii.cl/factura_electronica/factura_mercado/boletas_elec_0720_3.pdf)
- [Diagrama del XML de boletas](https://www.sii.cl/factura_electronica/factura_mercado/diag_boleta_0920.zip)

Para ver cómo se usan, revisa los ejemplos de la API con la operación `examples/get` (ver [Tu primera llamada](https://www.apigateway.cl/academy/emision-de-dte/preparacion/tu-primera-llamada)) y los [casos prácticos](https://www.apigateway.cl/academy/emision-de-dte/casos-practicos).

## La solicitud

```
POST https://app.apigateway.cl/api/v2/libredte/billing/document/builder/build
```

La llamada es la misma para todos los documentos, incluidas las boletas; cambian los datos del documento (`parsedData`) y el CAF.

La "bolsa" (`bag`) agrupa todo lo que se necesita para construir el documento:

| Campo | Qué es |
|-------|--------|
| `parsedData` | Los datos del documento, en JSON (ver [Los datos del documento](#los-datos-del-documento)). |
| `caf` | El XML del CAF en base64. Debe ser del mismo tipo de documento: de facturas para una factura, de boletas para una boleta. |
| `certificate` | El certificado digital (ver [El certificado digital](https://www.apigateway.cl/academy/emision-de-dte/preparacion/certificado-digital)). |

**La fecha de emisión** va en `IdDoc.FchEmis` (`AAAA-MM-DD`). Si no la envías, la API usa la fecha del día. Guárdala junto al documento: la necesitarás para corregirlo con una nota de crédito (`FchRef`) y para verificarlo en el SII. También la encuentras en la respuesta, en `data.document.Encabezado.IdDoc.FchEmis`.

Estos son los ejemplos de cada caso:

### 1.1. Facturas, notas y guías

```json
{
    "parameters": {
        "bag": {
            "parsedData": {
                "Encabezado": {
                    "IdDoc": { "TipoDTE": 33, "Folio": 1, "FmaPago": 1 },
                    "Emisor": {
                        "RUTEmisor": "88888888-8",
                        "RznSoc": "Mi Empresa SpA",
                        "GiroEmis": "Venta de software",
                        "Acteco": 620100,
                        "DirOrigen": "Av. Siempre Viva 742",
                        "CmnaOrigen": "Providencia"
                    },
                    "Receptor": {
                        "RUTRecep": "77777777-7",
                        "RznSocRecep": "Cliente Ejemplo Ltda.",
                        "GiroRecep": "Comercio",
                        "DirRecep": "Calle Falsa 123",
                        "CmnaRecep": "Santiago"
                    }
                },
                "Detalle": [
                    { "NmbItem": "Servicio de desarrollo", "QtyItem": 1, "PrcItem": 100000 }
                ]
            },
            "caf": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/Pgo8QVVU...",
            "certificate": {
                "certificate": "-----BEGIN CERTIFICATE-----\nMIIG...\n-----END CERTIFICATE-----",
                "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----"
            }
        }
    }
}
```

El precio (`PrcItem`) va **neto**. No hay totales: la API calcula neto $100.000, IVA $19.000 y total $119.000.

### 1.2. Boletas

```json
{
    "parameters": {
        "bag": {
            "parsedData": {
                "Encabezado": {
                    "IdDoc": { "TipoDTE": 39, "Folio": 1 },
                    "Emisor": {
                        "RUTEmisor": "88888888-8",
                        "RznSoc": "Mi Empresa SpA",
                        "GiroEmis": "Venta de artículos de computación",
                        "DirOrigen": "Av. Siempre Viva 742",
                        "CmnaOrigen": "Providencia"
                    },
                    "Receptor": {
                        "RUTRecep": "66666666-6",
                        "RznSocRecep": "Cliente final",
                        "DirRecep": "Santiago",
                        "CmnaRecep": "Santiago"
                    }
                },
                "Detalle": [
                    { "NmbItem": "Mouse inalámbrico", "QtyItem": 2, "PrcItem": 11900 }
                ]
            },
            "caf": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/Pgo8QVVU...",
            "certificate": {
                "certificate": "-----BEGIN CERTIFICATE-----\nMIIG...\n-----END CERTIFICATE-----",
                "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----"
            }
        }
    }
}
```

La API calcula un total de $23.800, del que $20.000 es neto y $3.800 es IVA. Lo que cambia respecto de una factura:

| Dato | En una boleta |
|------|---------------|
| `TipoDTE` | `39`, o `41` si es una boleta exenta. |
| `PrcItem` | **Con IVA incluido.** |
| Receptor | Opcional. Si el cliente no da su RUT, usa `66666666-6`. |
| `FmaPago`, `Acteco`, `GiroRecep` | No se usan. |
| `RznSoc`, `GiroEmis` | Se envían igual que en una factura. En el XML de la boleta la API los escribe como `RznSocEmisor` y `GiroEmisor`, los nombres que usa el SII. |
| `caf` | Un CAF de boletas (`39`, o `41` para la exenta). |

En una **boleta exenta** (`41`), marca cada ítem con `IndExe: 1`: la API calcula `MntExe` con el total, sin IVA.

## Qué se obtiene según lo que envíes

| Envías | Obtienes | Sirve para |
|--------|----------|------------|
| Solo `parsedData` | Un **borrador**, sin timbre ni firma | Previsualizar los totales o el PDF de una cotización |
| `parsedData` + `caf` | Un documento **timbrado**, sin firma | (Poco uso práctico) |
| `parsedData` + `caf` + `certificate` | Un documento **timbrado y firmado** | **Emitir**: es lo que se envía al SII |

Para emitir, envía siempre los tres.

## La respuesta

Esta es la respuesta para la factura del ejemplo, construida con un CAF del ambiente de certificación. Se muestran los campos que usarás; la tabla de abajo describe todos. Los valores largos (XML en base64, llaves y certificados) están acortados con `...`:

```json
{
    "data": {
        "document_id": "88888888-8_T033F000000001",
        "document_xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0i...",
        "document": {
            "Encabezado": {
                "IdDoc": {
                    "TipoDTE": 33,
                    "Folio": 1,
                    "FchEmis": "2026-10-29",
                    "TpoTranVenta": 1,
                    "FmaPago": 1
                },
                "Emisor": {
                    "RUTEmisor": "88888888-8",
                    "RznSoc": "Mi Empresa SpA",
                    "GiroEmis": "Venta de software",
                    "Acteco": 620100,
                    "DirOrigen": "Av. Siempre Viva 742",
                    "CmnaOrigen": "Providencia"
                },
                "Receptor": {
                    "RUTRecep": "77777777-7",
                    "RznSocRecep": "Cliente Ejemplo Ltda.",
                    "GiroRecep": "Comercio",
                    "DirRecep": "Calle Falsa 123",
                    "CmnaRecep": "Santiago"
                },
                "Totales": {
                    "MntNeto": 100000,
                    "TasaIVA": 19,
                    "IVA": 19000,
                    "MntTotal": 119000
                }
            },
            "Detalle": [
                {
                    "NroLinDet": 1,
                    "NmbItem": "Servicio de desarrollo",
                    "QtyItem": 1,
                    "PrcItem": 100000,
                    "MontoItem": 100000
                }
            ]
        },
        "document_normalized": {
            "Encabezado": {
                "Totales": {
                    "MntNeto": 100000,
                    "MntExe": null,
                    "TasaIVA": 19,
                    "IVA": 19000,
                    "ImptoReten": null,
                    "CredEC": null,
                    "MntTotal": 119000,
                    "TpoMoneda": null
                }
            },
            "...": "todos los campos del formato"
        },
        "document_type": {
            "codigo": 33,
            "nombre": "Factura electrónica",
            "...": "..."
        },
        "certificate": {
            "id": "11222333-9",
            "name": "Daniel Bot",
            "...": "...",
            "pkey": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
        },
        "caf": {
            "id": "CAF33D1H100",
            "...": "...",
            "privateKey": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
        },
        "...": "document_parsed, document_stamp, timbre, document_auth, document_extra, emisor, receptor y options"
    }
}
```

| Campo | Qué es | ¿Guardarlo? |
|-------|--------|-------------|
| `document_xml` | **El XML del DTE timbrado y firmado, en base64.** Es el documento con validez tributaria. | Sí, siempre |
| `document_id` | Identificador del documento: emisor, tipo y folio. | Sí |
| `document` | Los datos del documento tal como quedaron en el XML, solo con los campos que tienen valor. | Opcional |
| `document_normalized` | Los mismos datos con todos los campos del formato; los que no se usan vienen en `null`. Incluye los totales calculados. | Opcional |
| `document_parsed` | Los datos tal como los enviaste en `parsedData`. | No |
| `document_type` | Datos del tipo de documento: nombre, si es cedible, si es exento, etc. | No |
| `document_stamp` | El timbre electrónico (TED) en XML, en base64. Ya está dentro de `document_xml`. | No |
| `timbre` | El mismo timbre electrónico, en formato JSON. | No |
| `document_auth` | La resolución del emisor, si la indicaste. | No |
| `document_extra` | Datos adicionales del documento, si los hay. | No |
| `emisor`, `receptor` | Los datos del emisor y del receptor que usó la API. | No |
| `certificate` | Los datos del certificado usado, **incluida su llave privada** (`pkey`). | **No** |
| `caf` | Los datos del CAF usado, **incluida su llave privada** (`privateKey`). | **No** |
| `options` | Las opciones que usó cada etapa de la construcción. | No |

> [!WARNING] Guarda solo lo necesario
>
> La respuesta incluye los datos del certificado y del CAF que se usaron, con sus llaves. No guardes la respuesta completa en tu base de datos ni en tus logs: guarda el `document_xml`, el `document_id` y, si los necesitas, los totales.

Decodificado, `document_xml` se ve así (resumido):

```xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<DTE xmlns="http://www.sii.cl/SiiDte" version="1.0">
  <Documento ID="LibreDTE_88888888-8_T033F000000000001">
    <Encabezado>
      <IdDoc><TipoDTE>33</TipoDTE><Folio>1</Folio><FchEmis>2026-10-29</FchEmis></IdDoc>
      <Emisor>...</Emisor>
      <Receptor>...</Receptor>
      <Totales><MntNeto>100000</MntNeto><TasaIVA>19</TasaIVA><IVA>19000</IVA><MntTotal>119000</MntTotal></Totales>
    </Encabezado>
    <Detalle>...</Detalle>
    <TED version="1.0">...</TED>            <!-- timbre electrónico -->
    <TmstFirma>2026-10-29T16:57:50</TmstFirma>
  </Documento>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">...</Signature>  <!-- firma -->
</DTE>
```

En una **boleta**, la respuesta tiene los mismos campos. `document_type` trae el código `39` ("Boleta electrónica") y el encabezado del XML usa los nombres de las boletas:

```xml
<IdDoc><TipoDTE>39</TipoDTE><Folio>1</Folio><FchEmis>2026-11-04</FchEmis><IndServicio>3</IndServicio></IdDoc>
<Emisor><RUTEmisor>88888888-8</RUTEmisor><RznSocEmisor>Mi Empresa SpA</RznSocEmisor><GiroEmisor>...</GiroEmisor>...</Emisor>
<Totales><MntNeto>20000</MntNeto><IVA>3800</IVA><MntTotal>23800</MntTotal></Totales>
```

`IndServicio` indica el tipo de venta; si no lo envías, la API pone `3` (venta de bienes y servicios).

> [!INFO] El XML no se modifica
>
> Una vez firmado, el XML no se puede tocar: cualquier cambio, aunque sea un espacio, invalida la firma. Guarda el base64 tal como llega, o decodifícalo a bytes sin convertir la codificación (es ISO-8859-1, no UTF-8).

## Errores frecuentes en este paso

| Mensaje (`detail`) | Causa | Solución |
|--------------------|-------|----------|
| `El XPath "//Encabezado/IdDoc/Folio" ... es obligatorio: no coincidió con ningún nodo.` | Falta `IdDoc.Folio`. | Asigna el folio desde tu control de folios. |
| `El folio N del documento ... no está disponible en el rango del CAF ...` | El folio no está dentro del CAF enviado. | Usa un folio del rango, o el CAF que corresponde a ese folio. |
| `El CAF ... fue autorizado al emisor ..., pero el documento ... es del emisor ...` | El CAF es de otra empresa. | Usa un CAF solicitado por la empresa que emite el documento. |
| `El CAF ... es del tipo de documento ..., pero el documento ... es del tipo ...` | El CAF es de otro tipo de documento, por ejemplo uno de facturas (`33`) para una boleta (`39`). | Usa el CAF del mismo tipo que el documento. |
| `The verification digit of the RUT ... is incorrect.` | Un RUT tiene mal el dígito verificador. | Valida los RUT en tu sistema antes de emitir. |
| `Falta incluir documento de referencia.` | Es una nota de crédito o débito sin `Referencia`. | Agrega la referencia al documento que corrige. |
| `No se encontró un deserializador capaz de construir una instancia de ...CafInterface ... requiere datos codificados en base64 válido.` | El `caf` no viene en base64 válido. | Codifica el XML del CAF en base64, en una línea. |
| `Se requieren los datos del certificado ...` | El certificado viene incompleto. | Envía `certificate` + `privateKey` (PEM), o `data` + `password` (`.p12`). |

> [!WARNING] Un CAF por emisor y por tipo de documento
>
> El CAF debe ser del **mismo emisor** y del **mismo tipo de documento**: una boleta (`39`) se timbra con un CAF de boletas, no con uno de facturas. Si no coinciden, la API rechaza la construcción con uno de los dos errores de la tabla.


---
Última actualización el 05/11/2026

