La boleta electrónica se instaló como obligación general en Chile el 1 de marzo de 2021, cerrando un proceso gradual que comenzó en 2019 con contribuyentes grandes y descendió por tamaño hasta cubrir la totalidad del comercio. La transición eliminó el rollo térmico impreso desde caja registradora como comprobante válido y forzó a todo negocio que vende al consumidor final —desde el kiosco de la esquina hasta el e-commerce transnacional— a emitir un XML firmado y enviarlo al SII antes de cerrar la venta. Cinco años después, la implementación técnica en la mayoría de las empresas sigue siendo frágil, particularmente en el flujo que más tráfico procesa: el checkout online donde el cliente paga con Webpay y nunca entrega su RUT.

El argumento de este artículo es que emitir una boleta electrónica correctamente en un flujo de e-commerce chileno es más simple de lo que las integraciones actuales sugieren, si se acepta una decisión de diseño incómoda: cuando el comprador no entrega RUT, hay que emitir la boleta a "consumidor final" (RUT 66666666-6) y no intentar capturar el dato bloqueando el pago. Los siguientes párrafos describen el flujo técnico completo, el caso Webpay en profundidad, los tres errores que producen la mayoría de las boletas rechazadas por el SII, y las decisiones que suelen postergar startups y terminar reescribiendo bajo presión.

Qué distingue a la boleta de la factura, técnicamente

La boleta electrónica (DTE 39 afecta a IVA, DTE 41 exenta) comparte estructura general con la factura pero tiene tres diferencias operativas que importan al integrar:

El receptor es opcional. Una factura no puede emitirse sin RUT, razón social, giro y dirección del comprador. Una boleta puede emitirse sin ningún dato del comprador, o con solo el RUT. Esto refleja la naturaleza tributaria del documento: la boleta acredita venta al consumidor final, no genera crédito fiscal para el comprador, y por lo tanto el fisco no necesita saber quién compró.

El IVA va incluido en el precio. En una factura el precio unitario es neto y el sistema calcula el IVA aparte. En una boleta el precio unitario es bruto y el IVA queda desglosado internamente en el XML pero no aparece en la línea de detalle mostrada al cliente. Esto se alinea con la práctica comercial: los precios de venta al público se cotizan siempre con IVA incluido.

El envío al SII es en lote y con timing distinto. Cada boleta se envía al SII individualmente al momento de emitir, pero adicionalmente el emisor debe generar y enviar un reporte de consumo de folios (RCOF) al día siguiente hábil. El emisor DTE gestiona esto internamente; el integrador solo emite y olvida.

Payload mínimo de una boleta electrónica

El request para emitir una boleta con la API de yamt.com es análogo al de una factura, con dos diferencias: el campo type vale 39, y el bloque receiver puede omitirse por completo o llenarse parcialmente:

curl -X POST https://app.yamt.com/api/76123456-7/emitir \
  -H "Authorization: Bearer yamt_TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": 39,
    "items": [
      { "name": "Zapatillas modelo Alpha talla 42", "qty": 1, "price": 89990 }
    ]
  }'

El precio 89990 es el precio bruto que el cliente pagó. El motor de emisión descompone internamente: neto 75622, IVA 14368, total 89990. La respuesta llega con el mismo formato que la factura, incluyendo pdf_url y xml_url firmados:

{
  "ok": true,
  "env": "live",
  "document": {
    "id": 15982,
    "type": 39,
    "type_name": "Boleta electrónica",
    "folio": 8432,
    "date": "2026-08-12",
    "issuer": { "rut": "76123456-7", "name": "Comercio Ejemplo SpA" },
    "receiver": null,
    "amounts": {
      "net": 75622,
      "tax": 14368,
      "tax_rate": 19,
      "exempt": 0,
      "total": 89990
    }
  },
  "sii": { "status": "enviado", "track_id": "9182736451" },
  "pdf_url": "https://app.yamt.com/api/76123456-7/pdf/39/8432?tk=xyz789",
  "xml_url": "https://app.yamt.com/api/76123456-7/xml/39/8432?tk=xyz789"
}

El caso Webpay: emitir boleta a consumidor final sin RUT

Webpay Plus, el gateway de Transbank que concentra la mayoría de los pagos online en Chile, no captura el RUT del comprador en su flujo estándar. El cliente ingresa datos de tarjeta, autoriza, y vuelve al comercio con un token de confirmación. Nunca hay campo RUT en ese flujo. La aplicación integradora, ante la obligación de emitir boleta, tiene tres opciones:

Opción 1: pedir RUT antes del pago. Es la más común y también la peor. Agregar un campo obligatorio de RUT en el checkout incrementa la tasa de abandono medible en cualquier tienda que lo pruebe. El cliente que iba a comprar sin registrarse abandona ante la fricción. Esta opción se implementa por miedo tributario y termina costando más en conversión perdida que en cumplimiento.

Opción 2: capturar RUT opcionalmente después del pago. Mejor. Cobrar primero, agradecer, y en la pantalla de gracias mostrar un formulario opcional "¿te enviamos boleta con tu RUT?". Si el cliente lo llena, boleta con RUT. Si no, boleta a consumidor final. Esto respeta la asimetría de intenciones: el que quiere descargar contable el pago está motivado a completar el formulario extra; el que compra para consumo personal no.

Opción 3: emitir siempre a consumidor final. Aceptable para tiendas donde la clientela es 100% B2C sin excepciones. El SII permite emitir boletas al RUT genérico 66666666-6 para consumidor final. La boleta es válida, el fisco la acepta, y elimina cualquier fricción del checkout. La única contraindicación es si el negocio tiene un porcentaje relevante de clientes B2B que van a exigir factura después; para esos casos conviene el patrón híbrido de la opción 2.

El código para la opción 3, integrado con el hook de confirmación de pago de Webpay, se ve así en PHP:

<?php
declare(strict_types=1);

const CONSUMIDOR_FINAL_RUT = '66666666-6';

function emitirBoletaConsumidorFinal(array $items): array {
    $ch = curl_init('https://app.yamt.com/api/76123456-7/emitir');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . getenv('YAMT_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'type'  => 39,
            'items' => $items,
            'receiver' => [ 'rut' => CONSUMIDOR_FINAL_RUT ],
        ], JSON_UNESCAPED_UNICODE),
        CURLOPT_TIMEOUT => 15,
    ]);
    $body = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $data = json_decode($body, true);
    if ($code !== 200 || empty($data['ok'])) {
        throw new RuntimeException('Boleta falló: ' . ($data['error'] ?? "HTTP $code"));
    }
    return $data['document'];
}

// Webhook de confirmación Webpay
function onWebpayConfirmed(string $orderId, array $tbkResponse): void {
    if ($tbkResponse['response_code'] !== 0) return; // pago rechazado

    $order = db_get_order($orderId);
    $items = array_map(fn($line) => [
        'name'  => $line['product_name'],
        'qty'   => $line['qty'],
        'price' => $line['price'], // bruto, IVA incluido
    ], $order['lines']);

    try {
        $doc = emitirBoletaConsumidorFinal($items);
        db_save_boleta($orderId, $doc['folio'], $doc['id'], $doc['pdf_url']);
        enviarEmailBoleta($order['customer_email'], $doc['pdf_url']);
    } catch (Throwable $e) {
        db_mark_boleta_pending($orderId, $e->getMessage());
        alertOps('Boleta pendiente order ' . $orderId . ': ' . $e->getMessage());
    }
}

El patrón que este código refleja es importante: la emisión ocurre dentro del webhook de confirmación de pago, no en el hilo del checkout. Si la emisión falla, se registra pendiente y se alerta a operaciones, pero el cliente ya recibió su confirmación de pago y no ve nada raro. Un cron secundario reintenta las pendientes al cabo de minutos, y si persisten, un operador humano investiga la causa (folios agotados, certificado vencido, error de red).

Boleta con RUT: la variante para clientes que sí lo entregan

Si el flujo captura RUT (opcionalmente o siempre), el payload cambia mínimamente: se incluye receiver.rut y opcionalmente receiver.name. La API valida el RUT contra el algoritmo estándar de dígito verificador y rebota con 400 si es inválido. No se requieren giro, dirección ni email para boleta:

{
  "type": 39,
  "receiver": {
    "rut": "16543219-K",
    "name": "Juan Pérez"
  },
  "items": [
    { "name": "Suscripción Plan Pro mensual", "qty": 1, "price": 12990 }
  ]
}

Incluir el nombre del comprador no es obligatorio pero mejora la utilidad del PDF para el cliente. La ausencia de giro y dirección es correcta y esperable: la boleta no los requiere y agregar campos vacíos no ayuda a nadie.

Boleta exenta (DTE 41): cuándo aplica

La boleta exenta se emite cuando la operación está exenta de IVA por ley: ciertos servicios educacionales, servicios de salud prestados por profesionales inscritos, algunos servicios financieros, y las exportaciones. El payload es idéntico al de la boleta afecta, cambiando type a 41:

{
  "type": 41,
  "items": [
    { "name": "Consulta médica traumatología", "qty": 1, "price": 45000 }
  ]
}

El motor de emisión no aplica IVA y todo el monto va a la columna exenta. Emitir DTE 41 para una operación que sí debía ser afecta es error tributario grave; conviene que el sistema no ofrezca al usuario final "boleta exenta" como opción libre, sino que se derive automáticamente del catálogo de productos o servicios cuya afectación fiscal está preclasificada.

Un caso mixto frecuente es una consulta médica que incluye venta de un producto afecto. La solución no es una boleta exenta con línea afecta; es dos boletas separadas, una exenta por el servicio y una afecta por el producto. Alternativamente, si la naturaleza de la operación lo permite, una boleta afecta con líneas exentas marcadas individualmente con exempt: true.

Los tres errores que producen la mayoría de las boletas rechazadas

Emitir en el hilo del checkout con timeout corto. El emisor tarda entre 200ms y 2s en responder; el SII puede tardar más si tiene degradación. Si la emisión está en el hilo del checkout con timeout de 3 segundos y ocurre un pico de latencia del SII, un porcentaje de las boletas queda a medio camino: cobradas al cliente pero sin folio ni PDF. La solución es emitir en el webhook de pago, no en el checkout.

Precios brutos vs precios netos confundidos. El error clásico: el catálogo guarda precio neto (porque el equipo comercial trabaja en neto), el checkout suma IVA para mostrar bruto al cliente, y la aplicación envía el neto al emisor pensando que es bruto. Resultado: la boleta emite por un monto 19% menor que lo que el cliente pagó, y las declaraciones mensuales al SII no cuadran con la facturación real. La regla es simple: para boleta DTE 39, el campo price es el bruto que el cliente paga. Si el catálogo está en neto, hay que sumar IVA antes de enviar.

Descripción de ítem con caracteres inválidos. El schema del SII acepta un subconjunto de caracteres en el campo de descripción. Comillas dobles, comillas simples y algunos caracteres unicode extendidos rompen el XML. El emisor típicamente sanitiza el input, pero un nombre de producto como Auriculares "Pro" con cable puede llegar mal al SII si la sanitización es imperfecta. La recomendación es normalizar nombres de productos en el catálogo (reemplazar comillas dobles por simples, evitar unicode extendido) antes de que lleguen al carrito.

Consulta de estado y descarga de la boleta

El endpoint de consulta de estado es el mismo que para factura, cambiando type a 39:

curl -X POST https://app.yamt.com/api/76123456-7/emision_estado_dte \
  -H "Authorization: Bearer yamt_TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": 39, "folio": 8432 }'

La descarga del PDF de la boleta se hace contra el mismo endpoint que factura, con la URL firmada que devuelve la emisión. Para envío por email al cliente, incluir el pdf_url directamente en el mensaje suele ser suficiente; si se quiere adjuntar como archivo, descargar el binario con ?base64=1 y adjuntar desde la aplicación.

Notas de crédito sobre boletas: qué se puede y qué no

Las boletas pueden anularse o corregirse por nota de crédito (DTE 61) siguiendo la misma lógica que las facturas. El campo refs apunta al DTE 39 original:

{
  "type": 61,
  "receiver": { "rut": "66666666-6" },
  "items": [
    { "name": "Anulación boleta 8432", "qty": 1, "price": 89990 }
  ],
  "refs": [
    {
      "type": 39,
      "folio": 8432,
      "date": "2026-08-12",
      "reason": "Anula documento"
    }
  ]
}

La restricción práctica es que la nota de crédito debe emitirse dentro del mismo período tributario mensual de la boleta original para simplificar la reconciliación. Anular una boleta del mes anterior es tributariamente válido pero contablemente complejo, y muchos ERPs no lo permiten sin autorización manual.

Volumen y arquitectura: cuándo esta integración deja de ser trivial

Emitir 100 boletas al día desde un e-commerce chico es un problema resuelto con las tres decisiones arquitectónicas correctas: emitir en el webhook, guardar el XML localmente, tolerar fallos con reintentos asíncronos. Emitir 100.000 boletas al día en un retail grande introduce complejidad adicional: encolar emisiones para no saturar al emisor, distribuir la carga en múltiples RUT si el negocio opera bajo varias razones sociales, monitorear tasas de rechazo del SII y alertar tempranamente, mantener un buffer de folios para no bloquearse en horas peak, y garantizar que la reconciliación diaria entre ventas y boletas sea automatizada.

Ninguna de esas piezas es responsabilidad del emisor, pero todas son responsabilidad de la aplicación integradora. La integración con el emisor sigue siendo simple; lo que crece con el volumen es la infraestructura alrededor. Esto es análogo a cualquier integración con proveedor externo: la llamada HTTP es trivial, el manejo operacional es donde vive la ingeniería real.

Cuándo esta guía no aplica

Esta guía cubre el flujo B2C típico de e-commerce, retail y servicios al consumidor final. Para operaciones B2B —venta a empresas, servicios profesionales facturados a otras empresas, cualquier operación donde el comprador va a descargar el IVA como crédito fiscal— la boleta no es el documento correcto; corresponde factura electrónica. El artículo dedicado sobre cómo emitir facturas electrónicas por API cubre ese caso con el mismo nivel de detalle. Para decidir en qué operación aplica cada documento, la referencia es boleta o factura electrónica en Chile: cuándo emitir cada una.

Este artículo se basa en la Resolución Exenta N° 74 de 2020 del SII (obligatoriedad de boleta electrónica), la Ley 20.727 y las circulares del SII entre 2020 y 2025, la documentación técnica de docs.yamt.com/dte, la documentación pública de Transbank para Webpay Plus, y la experiencia operacional de MOX Networks emitiendo boletas para su servicio de VPN a consumidor final con integración Webpay desde 2022. La firma opera su emisión sobre yamt.com y ofrece la integración como servicio a terceros.