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.
Instala la skill
Sección titulada «Instala la skill»Este sitio publica el Agent Skill simplo-invoicing en
/.well-known/skills/:
npx skills add https://docs.simplo.clLa 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.
Instalar y configurar
Sección titulada «Instalar y configurar»npm install @simplohq/sdk # o pnpm add / yarn add / bun add- Requiere Node ≥ 18.17 (
fetchglobal). Cero dependencias en runtime. - Credencial: variable de entorno
SIMPLO_API_KEY(formatosk_simplo_...). Solo backend — nunca expongas la key en un bundle de navegador, nunca la commitees. Agrégala al.env.exampledel 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 faltaCrea el cliente una vez (module scope / contenedor DI), no por request.
El flujo canónico de emisión
Sección titulada «El flujo canónico de emisión»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:
- Los montos son pesos chilenos (CLP) enteros, netos de IVA. Sin centavos, sin floats para dinero. La API calcula el IVA (19%).
emitretorna antes del veredicto del SII. Persistedte.id, tratafirmadocomo “en vuelo”, y resuelve el estado final vía webhooks (preferido) o polling desimplo.dtes.retrieve(id)hasta queestadoseaaceptado,rechazadoocon_reparos.- Guarda
dte.idydte.folioen tu lado (ej. en la fila de la orden) inmediatamente después del emit. - No bloquees el flujo del usuario en la aceptación del SII — puede tardar. Emite, responde, reconcilia asíncronamente.
Manejo de errores — por clase
Sección titulada «Manejo de errores — por clase»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.
Reglas de idempotencia
Sección titulada «Reglas de idempotencia»dtes.emitauto-genera unaIdempotency-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
ValidationErrorconcode: 'IDEMPOTENCY_BODY_MISMATCH'.
Multi-empresa
Sección titulada «Multi-empresa»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 anteWebhookVerificationError. - 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.
Testing
Sección titulada «Testing»- 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:
- 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. - Inyecta un
fetchmock en el cliente (new Simplo({ apiKey, fetch: mockFetch })) cuando quieras ejercitar la serialización real.
- Mockea el cliente del SDK (
- Asegura con asserts:
tipo_dtecorrecto, montos CLP enteros, RUT pasado sin tocar,dte.id/foliopersistidos, y los caminos de error deValidationErroryConflictError. - 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/sdkinstalado; cliente creado una vez, key desdeSIMPLO_API_KEY;.env.exampleactualizado; sin key en código ni frontend. - Camino de emisión implementado con montos CLP enteros netos y el
tipo_dtecorrecto para el caso de uso (33 factura B2B / 39 boleta). -
dte.idyfoliopersistidos 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
estadoterminal. - Existe el camino
rechazado: notificación al operador o flujo correctivo — nunca silencioso. - Los reintentos entre procesos llevan una
idempotencyKeyestable. - Clases de error manejadas según la tabla;
requestIdincluido 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.