Skip to content
SimploSimplo Docs

Para coding agents

Playbook autocontenido para la tarea "integra la facturación electrónica de Simplo en esta app" — flujo canónico de emisión, errores, idempotencia, testing y checklist.

This content is not available in your language yet.

Instrucciones para un coding agent cuya tarea es “integra la facturación electrónica de Simplo (DTEs chilenos) en esta aplicación”. Esta página es autocontenida; los docs enlazados agregan profundidad, no prerrequisitos.

Estas docs también existen en formato máquina: /llms.txt es un índice real en formato llmstxt.org — anota cada página del sitio y cada operación de la API con su enlace markdown. Toda página HTML tiene su espejo en markdown puro agregando index.md a la URL (ej. /comienza/quickstart/index.md), la referencia completa de la API vive en un solo archivo en /api-reference.md, y el repo del SDK trae su propio AGENTS.md y llms.txt.

Este sitio publica el Agent Skill simplo-invoicing en /.well-known/skills/:

Ventana de terminal
npx skills add https://docs.simplo.cl

La skill le entrega a tu agente este mismo playbook (instalación, flujo canónico de emisión, errores por clase, idempotencia, testing y checklist) más referencias de tipos de DTE, errores, multi-empresa y webhooks — con los mismos límites honestos: la escritura vía API key está en beta privada ([email protected]) y ninguna key ni endpoint debe inventarse.

Vocabulario de dominio: DTE = Documento Tributario Electrónico. SII = la autoridad tributaria chilena. RUT = identificador tributario chileno (76543210-K). Folio = número de documento autorizado por el SII. CAF = el archivo del SII que autoriza un rango de folios.

Ventana de terminal
npm install @simplohq/sdk # o pnpm add / yarn add / bun add
  • Requiere Node ≥ 18.17 (fetch global). Cero dependencias en runtime.
  • Credencial: variable de entorno SIMPLO_API_KEY (formato sk_simplo_...). Solo backend — nunca expongas la key en un bundle de navegador, nunca la commitees. Agrégala al .env.example del proyecto como nombre, sin valor.
  • La API de Simplo está en beta privada; si el usuario no tiene key, dile que pida acceso en [email protected]. No inventes una key ni caigas en endpoints falsos.
import Simplo from '@simplohq/sdk';
export const simplo = new Simplo(); // lee SIMPLO_API_KEY, lanza si falta

Crea el cliente una vez (module scope / contenedor DI), no por request.

Toda integración de facturación se reduce a este flujo. Adapta los nombres, conserva la forma:

import Simplo, { ValidationError, type DteSummary } from '@simplohq/sdk';
const simplo = new Simplo();
export async function emitInvoice(order: {
customerRut: string; // ej. '76543210-K'
customerName: string;
items: Array<{ name: string; quantity: number; unitPriceClp: number }>;
}): Promise<DteSummary> {
const dte = await simplo.dtes.emit({
tipo_dte: 33, // 33 factura (B2B) — 39 para boletas a consumidor
receptor: {
rut: order.customerRut,
razon_social: order.customerName,
},
detalle: order.items.map((item) => ({
nombre: item.name,
cantidad: item.quantity,
precio: item.unitPriceClp, // CLP entero, NETO (el IVA lo agrega la API)
})),
metadata: { order_id: String(order.customerRut) }, // tu correlación propia
});
// dte.estado === 'firmado': firmado y en cola. La aceptación del SII es ASÍNCRONA.
return dte;
}

Reglas que hacen esto correcto:

  1. Los montos son pesos chilenos (CLP) enteros, netos de IVA. Sin centavos, sin floats para dinero. La API calcula el IVA (19%).
  2. emit retorna antes del veredicto del SII. Persiste dte.id, trata firmado como “en vuelo”, y resuelve el estado final vía webhooks (preferido) o polling de simplo.dtes.retrieve(id) hasta que estado sea aceptado, rechazado o con_reparos.
  3. Guarda dte.id y dte.folio en tu lado (ej. en la fila de la orden) inmediatamente después del emit.
  4. No bloquees el flujo del usuario en la aceptación del SII — puede tardar. Emite, responde, reconcilia asíncronamente.
import {
AuthenticationError,
ConflictError,
RateLimitError,
ServerError,
ConnectionError,
ValidationError,
} from '@simplohq/sdk';
Capturaste Haz esto
ValidationError Bug o input malo del usuario. Muestra error.details (field/code/message). Nunca reintentes tal cual. Común: INVALID_RUT — valida el dígito verificador del RUT en tu capa de formularios.
AuthenticationError Key mal configurada. Falla rápido con una pista de setup; no reintentes.
PermissionError La key no tiene el permiso/rol. Avísale al operador; no reintentes.
NotFoundError ID equivocado o recurso de otra empresa. Trátalo como bug de datos.
ConflictError Si code === 'IDEMPOTENCY_IN_PROGRESS': la misma emisión ya corre — espera y re-consulta en vez de re-emitir. En otro caso (ej. folio duplicado), muéstralo.
RateLimitError El SDK ya reintentó. Encola/frena al llamador; usa error.retryAfter.
ServerError / ConnectionError El SDK ya reintentó las requests seguras. Loguea con error.requestId y falla el job para que tu cola reintente después.

Tabla completa: Errores.

  • dtes.emit auto-genera una Idempotency-Key (UUID) por llamada — un reintento en vuelo nunca puede duplicar una factura. No construyas una capa extra de dedup alrededor de llamadas individuales.
  • Para dedup entre procesos (job reintentado por tu cola), pasa tu propia key estable: simplo.dtes.emit(params, { idempotencyKey: order-${orderId} }). La misma key + el mismo body dentro de 24h devuelve el resultado original.
  • Nunca reutilices una idempotency key con un body distinto — eso es un ValidationError con code: 'IDEMPOTENCY_BODY_MISMATCH'.

Una API key = una empresa. Si la app atiende varias empresas, mantén una key por empresa (variables de entorno / entradas de secretos separadas) e instancia un cliente por empresa. No multiplexes mutando un cliente compartido. La opción companyId existe para credenciales de plataforma, que están en el roadmap — no dependas de ella con keys normales. Detalles: Multi-empresa.

Webhooks (cuando la tarea incluye actualizaciones de estado)

Sección titulada «Webhooks (cuando la tarea incluye actualizaciones de estado)»

Sigue Webhooks al pie de la letra. No negociables:

  • Verifica cada entrega con simplo.webhooks.verify(rawBody, headers, secret) antes de confiar en ella; rechaza con 401 ante WebhookVerificationError.
  • Verifica contra el body crudo de la request (ej. express.raw), nunca JSON re-serializado.
  • Maneja reentregas (deduplica por webhook-id) y eventos fuera de orden.
  • Guarda el secreto de firma de webhooks.create — se muestra una sola vez.
  • Nunca llames a la API real desde tests. No existe una key de prueba que lo haga seguro — los documentos emitidos son documentos tributarios reales.
  • Testea tu integración mockeando en una de dos costuras:
    1. Mockea el cliente del SDK (vi.mock('@simplohq/sdk') / inyecta un fake con las mismas formas de método) — preferido para lógica de negocio.
    2. Inyecta un fetch mock en el cliente (new Simplo({ apiKey, fetch: mockFetch })) cuando quieras ejercitar la serialización real.
  • Asegura con asserts: tipo_dte correcto, montos CLP enteros, RUT pasado sin tocar, dte.id/folio persistidos, y los caminos de error de ValidationError y ConflictError.
  • Para handlers de webhook, firma payloads de prueba localmente (receta en Webhooks).

Definition of done — checklist de integración

Sección titulada «Definition of done — checklist de integración»
  • @simplohq/sdk instalado; cliente creado una vez, key desde SIMPLO_API_KEY; .env.example actualizado; sin key en código ni frontend.
  • Camino de emisión implementado con montos CLP enteros netos y el tipo_dte correcto para el caso de uso (33 factura B2B / 39 boleta).
  • dte.id y folio persistidos en el registro de dominio al emitir.
  • Veredicto asíncrono manejado: endpoint de webhook (verificado, body crudo, 401 ante firma mala) o un job de polling hasta un estado terminal.
  • Existe el camino rechazado: notificación al operador o flujo correctivo — nunca silencioso.
  • Los reintentos entre procesos llevan una idempotencyKey estable.
  • Clases de error manejadas según la tabla; requestId incluido en logs.
  • Tests: SDK/fetch mockeados, sin llamadas a la API real, caminos de error cubiertos.
  • Si es multi-empresa: un cliente por key de empresa; sin cliente mutable compartido.