Toda empresa que opera en Chile emite facturas electrónicas. Es obligación tributaria desde 2018, sin excepciones. Lo que sí es opcional es cómo se emiten: puedes construir tu propia integración con el SII —firma electrónica, folios CAF, XML según el formato oficial, cliente SOAP contra el web service— o puedes emitir facturas electrónicas por API con una sola llamada HTTP y olvidarte del resto.

Este artículo describe el segundo camino. Cómo se ve una integración real de facturación electrónica, cuánto código requiere efectivamente, qué decisiones te ahorra un emisor autorizado y qué errores frecuentes conviene prevenir desde el diseño. El objetivo es que un desarrollador que nunca ha tocado el flujo del SII pueda tener facturación funcionando en producción en un par de horas, y que un dueño de negocio entienda qué está pasando bajo el capó.

Qué hace un emisor DTE por ti

La lista de tareas que exige el SII para emitir una factura electrónica válida es larga: obtener un certificado digital, mantenerlo vigente, solicitar folios CAF al SII, gestionar bloques agotados, generar el XML según el formato oficial, firmarlo con el certificado, calcular timbre electrónico (TED), enviar el XML al web service de recepción, guardar el track ID, consultar aceptación posterior, conservar el archivo por seis años. Un emisor DTE autorizado absorbe todo eso y te expone una API simple: envías un JSON con los datos de la venta, recibes de vuelta el folio, el PDF y el XML firmado.

La ventaja no es solo de velocidad de integración; es también de mantenimiento. El SII publica varias circulares al año que actualizan el formato del XML, agregan campos, cambian validaciones. Tu emisor absorbe esos cambios; tu código no se entera. Cuando vence el certificado, lo renuevas en un lado y no tocas la aplicación. Cuando se agotan los folios, el emisor los solicita automáticamente.

El costo típico es por documento emitido o por plan mensual según volumen. Para volúmenes bajos suele ser marginal frente al costo de un desarrollador manteniendo la integración propia; para volúmenes altos, la matemática favorece igual si valoras el tiempo del equipo en proyectos que agreguen valor al negocio, no en gestionar la circular 47 del SII.

Emitir tu primera factura con una llamada HTTP

El siguiente es un ejemplo real usando la API de yamt. Emite una factura electrónica DTE 33 (afecta a IVA) a un cliente empresa por dos ítems:

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": 33,
    "receiver": {
      "rut": "12345678-9",
      "name": "Comercial Ejemplo Ltda",
      "activity": "Venta al por menor",
      "address": "Av. Siempre Viva 742",
      "city": "Santiago",
      "email": "pagos@ejemplo.cl"
    },
    "items": [
      { "name": "Asesoría mensual", "qty": 1, "price": 150000 },
      { "name": "Hosting dedicado", "qty": 12, "price": 8000 }
    ]
  }'

El type: 33 declara factura afecta a IVA. El emisor sale del path (/api/76123456-7/) y coincide con el RUT del certificado configurado en tu cuenta. Los ítems declaran nombre, cantidad y precio unitario neto: no calculas IVA, neto ni total, el motor lo hace por ti.

La respuesta llega en JSON con el folio asignado, el estado del envío al SII y URLs firmadas para descargar el PDF y el XML:

{
  "ok": true,
  "env": "live",
  "document": {
    "id": 4821,
    "type": 33,
    "type_name": "Factura electrónica",
    "folio": 137,
    "date": "2026-07-25",
    "issuer": { "rut": "76123456-7", "name": "Mi Empresa SpA" },
    "receiver": {
      "rut": "12345678-9",
      "name": "Comercial Ejemplo Ltda",
      "email": "pagos@ejemplo.cl"
    },
    "amounts": {
      "net": 246000,
      "tax": 46740,
      "tax_rate": 19,
      "exempt": 0,
      "total": 292740
    }
  },
  "sii": { "status": "enviado", "track_id": "9182736450" },
  "pdf_url": "https://app.yamt.com/api/76123456-7/pdf/33/137?tk=abc123",
  "xml_url": "https://app.yamt.com/api/76123456-7/xml/33/137?tk=abc123"
}

Con esto tienes factura emitida, enviada al SII y lista para entregar al cliente. Si incluiste receiver.email, el emisor le manda el PDF automáticamente. Si prefieres controlar la entrega, descargas el PDF desde el link firmado y lo envías por el canal que elijas.

El caso más común: facturación recurrente

Un SaaS que cobra mensualmente, una consultora que factura al cierre de mes, un hosting que renueva planes. Este es el patrón que más se implementa en Chile y el que más justifica automatizar la facturación electrónica. El siguiente snippet es un cron que corre el primer día de cada mes y factura todas las suscripciones activas:

<?php
declare(strict_types=1);

const YAMT_API   = 'https://app.yamt.com/api';
const YAMT_KEY   = 'yamt_TU_API_KEY';
const ISSUER_RUT = '76123456-7';

function emitirFactura(array $receiver, array $items): array {
    $ch = curl_init(YAMT_API . '/' . ISSUER_RUT . '/emitir');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . YAMT_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'type'     => 33,
            'receiver' => $receiver,
            'items'    => $items,
        ], JSON_UNESCAPED_UNICODE),
        CURLOPT_TIMEOUT => 30,
    ]);
    $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('Emisión falló: ' . ($data['error'] ?? "HTTP $code"));
    }
    return $data['document'];
}

// Cron mensual
foreach (obtenerSuscripcionesActivas() as $sub) {
    if (yaFacturadaEsteMes($sub['id'])) continue; // idempotencia
    try {
        $doc = emitirFactura(
            receiver: [
                'rut'      => $sub['rut'],
                'name'     => $sub['razon_social'],
                'activity' => $sub['giro'],
                'address'  => $sub['direccion'],
                'city'     => $sub['comuna'],
                'email'    => $sub['email'],
            ],
            items: 
                'name'  => 'Plan ' . $sub['plan'] . ' — ' . date('F Y'),
                'qty'   => 1,
                'price' => $sub['precio_neto'],
            ,
        );
        registrarFactura($sub['id'], $doc['folio'], $doc['id']);
    } catch (Throwable $e) {
        registrarError($sub['id'], $e->getMessage());
    }
}

Menos de 40 líneas para tener facturación mensual automatizada. Tres cosas importantes en este código:

Idempotencia. El check yaFacturadaEsteMes() evita doble facturación si el cron se reintenta. Sin esto, un reinicio del worker puede generar dos folios por la misma suscripción.

Fallo por suscripción, no por cron. El try/catch deja registrado el error de la suscripción problemática y continúa con las demás. Un mal RUT no tira abajo la facturación del mes entero.

El emisor asigna el folio. No manejas correlativos ni te preocupas del CAF. El document.folio que recibes es el que quedó registrado en el SII.

Anular o corregir: nota de crédito (DTE 61)

Una factura electrónica no se edita ni se borra una vez enviada al SII. La única forma de corregir o anular es emitir una nota de crédito que referencie el folio original. El payload es análogo al de factura, con un bloque refs:

{
  "type": 61,
  "receiver": {
    "rut": "12345678-9",
    "name": "Comercial Ejemplo Ltda"
  },
  "items": [
    { "name": "Anulación factura 137", "qty": 1, "price": 292740 }
  ],
  "refs": [
    {
      "type": 33,
      "folio": 137,
      "date": "2026-07-25",
      "reason": "Anula documento"
    }
  ]
}

El campo reason es el motivo según SII: 1 anula, 2 corrige texto sin afectar montos, 3 corrige montos. Tu UI puede mostrar "Anular" y "Corregir monto" al usuario final y traducir internamente al código correcto.

Consultar estado y descargar el documento

El envío al SII es asíncrono. Recibes el track_id cuando el SII acepta el envío para procesamiento, pero la aceptación final ocurre minutos después. Para consultar:

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": 33, "folio": 137 }'

La descarga del PDF y del XML usa las URLs firmadas que devuelve la emisión, o los endpoints directos:

curl "https://app.yamt.com/api/76123456-7/pdf/33/137?base64=1" \
  -H "Authorization: Bearer yamt_TU_API_KEY"

El parámetro ?base64=1 devuelve el binario codificado dentro del JSON, útil si vas a archivarlo sin exponer URL pública.

Los cinco errores que producen tickets al soporte

Sin folios CAF. Se agota el bloque y la emisión rebota con HTTP 422. Solución: monitorear el saldo y solicitar el próximo bloque cuando bajes del 20%. Con yamt puedes automatizar la solicitud vía POST /api/{rut}/folios_sii.

RUT malformado. El SII exige RUT con dígito verificador válido en formato XXXXXXXX-Y. Valida en el cliente antes de enviar.

Certificado vencido. El certificado de firma electrónica avanzada tiene vigencia limitada. Cuando vence, todas las emisiones fallan. Renuévalo con anticipación.

Datos del receptor incompletos. Factura DTE 33 requiere RUT, razón social, giro y dirección del receptor. Boleta DTE 39 no. Si migras de boleta a factura sin agregar campos, el request rebota con 400.

Sandbox en producción. Los emisores ofrecen ambiente de prueba y ambiente productivo. Loggea el campo env de la respuesta y alerta si en producción viene "sandbox".

Decisiones que se toman una vez

Emitir sincrónico o asíncrono. Salvo que necesites mostrar el folio en la pantalla de confirmación, emite asíncrono desde un webhook o cola. Bloquear el checkout dos segundos para esperar al SII es fricción evitable.

Guardar el XML localmente. El SII exige conservarlo seis años. El emisor lo hace en su lado, pero depender de un tercero para una obligación legal es fragilidad. Descarga y archiva en tu propio almacenamiento (S3, MinIO, disco con backup).

Multi-RUT si operas varios. Si tu negocio factura bajo múltiples razones sociales (holding, agencia, marketplace), yamt soporta múltiples RUT bajo una misma cuenta con API keys segmentadas por rol (owner, editor, viewer) y hasta 5 keys activas por cuenta.

Del ambiente de prueba a producción

El flujo típico para poner esto en producción se ve así:

  1. Creas cuenta en yamt.com y subes tu certificado digital.
  2. Generas API key con rol editor para tu ambiente de prueba.
  3. Emites documentos en sandbox: mismo endpoint, respuesta con env: "sandbox", no consume folios reales ni envía al SII.
  4. Solicitas folios CAF al SII directamente desde el panel de yamt o vía API.
  5. Cambias la API key por una de producción, y tus primeras facturas ya están saliendo con folios reales y llegando al SII.

Nada de esto requiere abandonar tu stack ni reescribir tu backend. La integración es un cliente HTTP y un puñado de funciones.

Esta guía cubre facturas, pero hay más

Este artículo se enfocó en factura electrónica afecta (DTE 33), que es el caso más común B2B. La misma API cubre factura exenta (DTE 34), guía de despacho (DTE 52), nota de débito (DTE 56) y nota de crédito (DTE 61); todas usan el mismo endpoint cambiando el campo type.

Para el otro caso mayoritario —boleta electrónica al consumidor final, típica en e-commerce y retail chileno— tenemos una guía dedicada: cómo emitir boletas electrónicas por API, incluyendo el caso Webpay sin RUT. Si aún no tienes claro cuál documento corresponde emitir en cada operación, empieza por boleta o factura electrónica en Chile: cuándo emitir cada una.

MOX Networks factura sus servicios de hosting, dominios y VPN sobre yamt.com desde 2018. La firma opera su propia emisión sobre esta plataforma y ofrece la integración como servicio a clientes que necesitan emitir DTE sin construir la infraestructura desde cero. Referencias técnicas: documentación oficial del SII, docs.yamt.com/dte.