Skip to content

Latest commit

 

History

79 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crafter Station

sunat-cli

Agent-first CLI for SUNAT tax automation in Peru. Built for AI agents as primary consumers, humans as supervisors.

npm Release License: MIT Built with Crafter Station sunat-cli.crafter.ing

What it does

Domain Coverage Surface
REST APIs (Consulta CPE, Padrón RUC, Tipo Cambio SBS) 90% OAuth2 client_credentials + scrape (cpe consulta, padron, tipo-cambio)
RHE + F616 (personas naturales, RUC 10) 95% agent-browser, no CAPTCHA
CPE Factura / Boleta / NC / ND 85% UBL 2.1 + XAdES-BES + SOAP directo
Resumen Diario + Comunicación de Baja 80% sendSummary + ticket polling
SIRE (RVIE Ventas + RCE Compras) 70% propuesta, ticket, descarga ZIP, TUS 1.0.0 import
GRE Remitente / Transportista 50% REST + JWT, modal 02 shipped
Drivers (mock, sunat-direct, facturador, PSE/OSE) 40% 2 of 5 — facturador / nubefact / apisperu shaped
CPE void T3 30% shaped, intent-token flow pending
Producción end-to-end 10% beta-only — never run prod blind

Overall: ~61% agent-ready across 9 SUNAT surfaces. Live coverage breakdown + roadmap on the website.

Requirements

  • Bun v1.2+
  • agent-browser v0.22+ (only for RHE / F616)
  • Chrome or Chromium (CDP transport for agent-browser)
  • For CPE / SIRE / GRE: a SUNAT digital certificate (PFX) + clave SOL

Install

bun add -g @crafter/sunat-cli

Run sunat-cli --help after installing. That list is the contract: every command it prints is available in the version you have.

Usage

Every section below is tagged npm, meaning it ships in the published release. A section tagged source would exist on main only. Right now nothing is source-only: the published release matches main.

CPE — Comprobantes Electrónicos (empresas, RUC 20) · npm

# Health check + driver info
sunat-cli cpe doctor --output json
sunat-cli cpe info

# Schema introspection (agents self-serve)
sunat-cli schema cpe-factura

# Always preview (T0 — no side effects)
sunat-cli cpe factura preview --params @factura.json

# Emit (T2 — requires --yes)
sunat-cli cpe factura emit --params @factura.json --yes

# Boleta / NC / ND — same shape
sunat-cli cpe boleta emit --params @boleta.json --yes
sunat-cli cpe nc emit --params @nota.json --yes
sunat-cli cpe nd emit --params @nota.json --yes

# Resumen diario (boletas <S/700)
sunat-cli cpe resumen send --fecha 2026-04-29 --yes
sunat-cli cpe resumen status --ticket 12345...

# Comunicación de Baja
sunat-cli cpe baja send --params @baja.json --yes

# GRE — Guía de Remisión Remitente (modal 02)
sunat-cli cpe gre emit --params @guia.json --yes
sunat-cli cpe gre status --ticket 12345...

# Consulta CPE (validación post-emisión, propia o de un proveedor)
sunat-cli cpe consulta --tipo 01 --serie F001 --numero 1234

SIRE — Sistema Integrado de Registros Electrónicos · npm

# RVIE (Ventas)
sunat-cli sire ventas periodos
sunat-cli sire ventas propuesta --periodo 202504
sunat-cli sire ventas ticket --id <ticket-id>
sunat-cli sire ventas archivo --ticket <ticket-id>    # downloads ZIP

# RCE (Compras)
sunat-cli sire compras propuesta --periodo 202504
sunat-cli sire compras importar --periodo 202504 --file ./compras.zip --yes

REST APIs · npm

# OAuth2 token for the SUNAT REST APIs
sunat-cli api token

Padrón RUC + Tipo de Cambio · npm

# Padrón RUC: download once (~370MB ZIP), then look up locally
sunat-cli padron status
sunat-cli padron sync
sunat-cli padron ruc 20123456789
sunat-cli padron batch --file ./rucs.csv

# Single RUC without syncing the padrón (drives the portal, ~5-10s)
sunat-cli padron ruc-online 20123456789

# Tipo de Cambio SBS
sunat-cli tipo-cambio --fecha hoy
sunat-cli tipo-cambio --fecha 2026-04-29 --output json

Secrets · npm

Secrets resolve from the environment first, then the OS keychain. Existing env-var setups keep working unchanged.

# Omit --value on a terminal and it prompts without echo, so the secret
# never lands in shell history
sunat-cli keychain set CPE_CERT_PASSWORD
sunat-cli keychain list
sunat-cli keychain clear CPE_CERT_PASSWORD

Multi-emisor profiles · npm

sunat-cli cpe profile set --name acme --ruc 20123456789 --razon-social "ACME SAC" --mode beta
sunat-cli cpe profile list
sunat-cli cpe profile use acme

Audit log · npm

# Inspect, optionally scoped to one emisor
sunat-cli audit list --ruc 20123456789 --limit 20

# Archive audit months older than the retention window
sunat-cli audit compact

# Delete archived months before a cutoff. Never runs on its own
sunat-cli audit prune --before 2025-01

RHE / F616 (personas naturales) · npm

# Login (browser, no CAPTCHA)
sunat-cli login

# Schema
sunat-cli schema rhe

# Dry-run first
sunat-cli rhe emit --dry-run --json '{"empresa":"Acme Corp.","monto":5000,"moneda":"PEN","medioPago":"TRANSFERENCIA","tipoDoc":"SIN DOCUMENTO","descripcion":"Servicios"}'

# Batch emit
sunat-cli rhe emit --batch ./data/example.csv

# F616 mensual (periodo en YYYY-MM). El ingreso lo precarga SUNAT desde tus
# RHE registrados: el CLI no lo escribe, asi que verificalo en el portal
sunat-cli f616 declare --dry-run --json '{"periodo":"2026-03"}'
sunat-cli f616 declare --json '{"periodo":"2026-03"}'

# Varios periodos de una
sunat-cli f616 declare --batch --months 2025-03..2026-02 --dry-run

# Lecturas del F616 por API, headless (abre el navegador solo si el token vencio)
sunat-cli f616 periodo 2026-03
sunat-cli f616 oficios

Design

Follows Agent DX principles and the agentskills.io specification.

  • --params @file.json over bespoke flags
  • --dry-run for all mutations (T0/T1)
  • --yes confirmation gate for irreversible ops (T2)
  • Intent-token flow for destructive ops (T3, e.g. cpe void)
  • --output json by default; NDJSON when piped
  • schema <command> introspection at runtime (25+ schemas)
  • Two-phase audit (pendingsuccess/error) on every write
  • Idempotency cache by natural key RUC-tipo-serie-numero
  • Input hardening against agent hallucinations

Headless after one login

SUNAT's monthly-declaration form looks server-rendered and behaves like one: driving the DOM never works, because the fields stay disabled until a background call returns. Underneath sits a JSON API, and reaching it needs a session token (IdCache) the portal mints only during its own browser login. A self-registered API client can never request that audience.

So the CLI captures the token once from the browser, then reads the API headless for its hour of life. f616 periodo and f616 oficios open the browser only when the cached token is missing or expired; otherwise they are plain HTTP. Verified with zero browser processes running.

The OAuth2 surfaces (SIRE, GRE, CPE, buzón) need no browser at all: their token comes from a password grant against api-seguridad.sunat.gob.pe with a client registered from the SOL menu.

Verification status

End-to-end verified against e-beta.sunat.gob.pe (2026-04-29):

  • Factura · cdrCode=0 Aceptado, hash deterministic
  • Boleta · cdrCode=0 Aceptado
  • Nota Crédito · FC01-555 Aceptado (Catálogo 09)
  • Nota Débito · FD01-777 Aceptado (Catálogo 10)
  • GRE Remitente modal 02 · ticket polling + JWT refresh ok
  • SIRE RVIE propuesta · ZIP descarga, parse correcto

Producción (e-factura.sunat.gob.pe): never run blind. Always preview first, then emit --dry-run, then --yes only with real cert + RUC. See packages/cli/LIMITATIONS.md for the full list of known boundaries.

Roadmap

Tracked in GitHub issues — milestones board.

Priority Issues
P0 — Now #18 Live verification against production
P1 — Next #10 cpe void T3 + safety rail · #12 Drivers nubefact + apisperu · #11 GRE modal 01 + Transportista
P2 — Later #13 Driver facturador · #14 SIRE reportes complementarios · #15 sqlite padrón index · #16 CI smoke jobs · #17 TUS auto-resume
P3 — Backlog #30 F.1683 arrendamiento

Research

3 SUNAT portals reverse-engineered. F616 input mask cracked via raw CDP WebSocket. reCAPTCHA bypassed through OAuth state exploitation. UBL 2.1 + XAdES-BES manually implemented (xml-crypto v6 had nested-signature quirks).

Findings:

Structure

packages/
  cli/       @crafter/sunat-cli — the CLI
  website/   sunat-cli.crafter.ing landing

Releasing

Publishing is automatic. Bump version in packages/cli/package.json and merge that PR to main. The release workflow then runs the test suite, publishes to npm with OIDC trusted publishing (no token stored in the repo), installs the published tarball to confirm it runs and exposes the expected commands, and finally tags the commit and opens a GitHub Release.

A merge that leaves version untouched publishes nothing.

License

MIT — Crafter Station, Lima, Peru

About

Agent-first CLI for SUNAT tax automation — gov-tech tool for Peruvian freelancers

Resources

Stars

25 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages