@ar-agents/facturacion
v0.3.0
Published
AFIP/ARCA factura electrónica (WSFE) as drop-in tools for the Vercel AI SDK. Emit Facturas A/B/C, query last authorized, validate authorized invoices. Reuses the WSAA infrastructure from @ar-agents/identity.
Maintainers
Readme
@ar-agents/facturacion
AFIP/ARCA factura electrónica (WSFE) for Vercel AI SDK 6+ agents.
Built for SaaS argentinos that need to:
- Emit Facturas A/B/C with the AFIP-issued CAE (Código de Autorización Electrónico)
- Auto-increment comprobante numbers via
consultarUltimoAutorizado - Verify previously-issued comprobantes
- Pre-validate locally to catch the 10 most common AFIP rejection reasons before the round-trip
Reuses the WSAA infrastructure from @ar-agents/identity: same X.509 cert, same TokenCache, same fetchWithRetry helper. If you already have @ar-agents/identity wired up, adding @ar-agents/facturacion is one cert-authorization step away.
Install
pnpm add @ar-agents/facturacion @ar-agents/identity
# peer deps: ai >=6, zod >=3Setup
- Generate cert + register alias as documented in
@ar-agents/identity. - In ARCA → "Administrador de Relaciones de Clave Fiscal" → "Nueva Relación" → "AFIP" → "WebServices" → "Servicio Web de Facturación Electrónica" → select your alias.
- Wire the client:
import { Experimental_Agent as Agent, stepCountIs } from "ai";
import { facturacionTools, WsfeClient } from "@ar-agents/facturacion";
const wsfe = new WsfeClient({
certPath: process.env.AFIP_CERT_PATH!,
keyPath: process.env.AFIP_KEY_PATH!,
cuit: process.env.AFIP_CUIT!,
env: "prod",
});
const agent = new Agent({
model: "anthropic/claude-sonnet-4-6",
tools: facturacionTools({ wsfe, defaultPtoVta: 1 }),
stopWhen: stepCountIs(6),
});
const { text } = await agent.generate({
prompt:
"Emití una Factura C de $12.100 a Juan Pérez (CUIT 23-30900000-9), servicio del 1/5 al 31/5, vencimiento 15/6.",
});The agent will: (1) consultar_ultimo_comprobante to get the next number, (2) emitir_factura, (3) report the CAE.
Tools
| Tool | Pure? | What it does |
| ----------------------------- | -------- | ------------------------------------------------------ |
| emitir_factura |: | Solicit a CAE for a new comprobante |
| consultar_ultimo_comprobante|: | Get the last authorized number for (PtoVta, CbteTipo) |
| consultar_factura_emitida |: | Look up a previously-issued comprobante |
| obtener_tipos_comprobante |: | Live AFIP catalog of comprobante types |
| obtener_tipos_documento |: | Live AFIP catalog of document types |
| obtener_alicuotas_iva |: | Live AFIP catalog of IVA rates |
| obtener_tipos_concepto |: | Live AFIP catalog of conceptos |
| obtener_tipos_moneda |: | Live AFIP catalog of currencies |
| obtener_cotizacion |: | AFIP exchange rate for a foreign currency vs ARS |
| health_check_afip |: | AFIP WSFE app/db/auth status |
All 10 tools require a WsfeClient. Without one, they return { available: false, error: <setup instructions> } instead of crashing: drop-in safe for stub deployments.
For pure-algorithm catalogs (no network), use the exported constants:
import { CbteTipo, DocTipo, AlicuotaIva, Concepto } from "@ar-agents/facturacion";
CbteTipo.FACTURA_C // 11
DocTipo.CUIT // 80
AlicuotaIva.VEINTIUNO // { id: 5, percent: 21 }
Concepto.SERVICIOS // 2Pre-flight validation
The library validates your SolicitarCaeInput LOCALLY before sending to AFIP, catching the most common rejection reasons:
ImpTotal≠ sum of components (AFIP error 10048)iva[].importesum ≠ImpIVA- Factura C with
ImpIVA > 0(Monotributo can't discriminate IVA) - Concepto = Servicios sin
fchServDesde/Hasta/VtoPago - Nota de Crédito/Débito sin
cbtesAsoc - Malformed
cbteFch(must beYYYYMMDD) monIdno-PES conmonCotiz = 1cbteHasta < cbteDesde
import { validateSolicitarCae } from "@ar-agents/facturacion";
const v = validateSolicitarCae(input);
if (!v.valid) {
// v.errors is an array of { field, message } in Spanish
console.error(v.errors);
}Saves you a network round-trip (and a CloudWatch entry) on every malformed request.
Robustez
The WsfeClient accepts requestTimeoutMs, maxRetries, and an onCall observability hook: all forwarded to both the WSAA token-refresh path and the per-comprobante WSFE path:
new WsfeClient({
certPath: "...",
keyPath: "...",
cuit: "20417581015",
env: "prod",
requestTimeoutMs: 15_000,
maxRetries: 2,
onCall: (e) => metrics.histogram("wsfe.duration", e.durationMs, { label: e.label }),
});License
MIT © Nazareno Clemente
Stability
This package is pre-1.0. Per npm convention, 0.x minor versions may include breaking changes. We document every breaking change in CHANGELOG.md under the corresponding minor bump and flag it explicitly. To avoid surprises:
# Pin to exact version (recommended for production):
pnpm add @ar-agents/<package>@<exact-version>We commit to no breaking changes within a patch version, and we publish 1.0.0 once the public API has stabilized across at least two consecutive minor releases.
