<< All versions

Skill v1.0.1

currentAutomated scan100/100
lapyme/facturas/docs

+31 new

──Details
PublishedOctober 30, 2026 at 03:54 PM
Content Hashsha256:0aac63e0352e81b6...
Git SHA7e5f6bb3314f
Bump Typepatch
Compare with v1.0.0
──Files
Files (1 file, 13.4 KB)
SKILL.md13.4 KBactive
SKILL.md · 291 lines · 13.4 KB

name: facturas description: "Emití facturas electrónicas, notas de crédito y notas de débito con ARCA (ex AFIP) desde TypeScript con el paquete facturas. Usala siempre que el usuario quiera facturar en Argentina, obtener un CAE, integrar ARCA o AFIP, WSFE, WSAA o WSMTXCA, emitir como monotributista o responsable inscripto, corregir o anular una factura, reintentar sin duplicar comprobantes o consultar un CUIT en el Padrón, aunque no nombre el paquete. También aplica a pedidos en inglés como Argentina e-invoicing, electronic invoice, AFIP integration o CAE authorization." license: Apache-2.0 metadata: version: "1.0.1"


facturas

facturas es el SDK de TypeScript para emitir comprobantes electrónicos con ARCA (ex AFIP). Se conecta directo desde tu servidor, sin SOAP ni XML a la vista y sin intermediarios. Requiere Node.js 22 o posterior y es solo ESM.

  • npm: facturas
  • Documentación: https://facturas-sdk.dev
  • Repositorio: https://github.com/LaPyme/facturas

No confíes en tu memoria

El paquete está antes de la 1.0 y su API cambia entre versiones menores. Por ejemplo, el CLI pasó de --tax-id a --cuit y varios métodos se renombraron o se quitaron. Lo que recuerdes de versiones anteriores, o de otras librerías de ARCA, probablemente no aplique.

  1. Mirá la versión instalada en node_modules/facturas/package.json y la

última con npm view facturas version. Si el proyecto está atrasado, avisale al usuario y leé el changelog.

  1. Los tipos en node_modules/facturas/dist/ son la verdad para la versión

instalada. Empezá por index.d.ts, que reexporta el resto, y buscá ahí cada campo antes de usarlo.

  1. Cualquier página de la documentación se lee en Markdown agregando .md a

la URL. El índice está en https://facturas-sdk.dev/llms.txt y el texto completo en https://facturas-sdk.dev/llms-full.txt.

  1. Si no encontrás respaldo en los tipos ni en la documentación, decilo. No

inventes campos ni códigos de ARCA.

La primera factura no necesita infraestructura

Sin base de datos, sin tabla y sin servicio externo. El cliente lee cuatro variables de entorno y guarda el ticket de WSAA en memoria:

VariableValor
ARCA_TAX_IDCUIT de 11 dígitos
ARCA_CERTIFICATE_PEMEl certificado PEM completo
ARCA_PRIVATE_KEY_PEMLa clave privada PEM completa
ARCA_ENVIRONMENTtest (homologación) o production. No hay valor por defecto
ts
import { createArcaClient } from "facturas";
const arca = createArcaClient();
const factura = await arca.issue({
issuer: "monotributo",
salesPoint: 3,
to: { condition: "consumidor_final" },
items: [{ amount: 150_000 }], // ARS 1.500,00 en centavos
});

El certificado, la autorización del servicio y el punto de venta se habilitan en ARCA. El SDK no lo hace por el usuario, pero el CLI lo guía:

sh
npx facturas init --cuit 20123456786 --env test # clave privada y CSR
npx facturas cert # guarda el certificado de ARCA
npx facturas check # prueba configuración, WSAA, WSFE y puntos de venta
npx facturas issue --sales-point 3 --issuer monotributo # factura de ARS 1 en homologación

Cuando algo falle antes de emitir, sugerí npx facturas check: nombra el paso que falla y dónde corregirlo en ARCA. En ejemplos usá siempre el CUIT de muestra 20123456786, nunca uno real.

El input es de negocio

Pasá datos de la venta y dejá que el SDK derive la clase, el tipo de comprobante, el IVA de cabecera y el pedido para ARCA. No armes esos valores a mano.

  • issuer es la condición fiscal real del emisor: "monotributo",

"responsable_inscripto", "exento" o "no_alcanzado", o su código de ARCA (6, 1, 4, 15). Es una afirmación legal: preguntala si no la sabés, nunca la infieras.

  • Un responsable inscripto emite A a receptores RI o monotributistas y B al

resto. Los demás emisores emiten C.

  • Los importes son enteros en centavos. 150_000 es ARS 1.500,00. Nunca uses

pesos con decimales.

  • Clase C: items: [{ amount }]. Clase A o B: items: [{ gross, vat }] o

[{ net, vat }], con vat en 0, 2.5, 5, 10.5, 21, 27, "exempt" o "untaxed".

  • to es el receptor: { condition: "consumidor_final" }, o una condición

con cuit. Desde ARS 10.000.000 el consumidor final también necesita cuit o dni.

  • Por defecto la fecha es hoy en Buenos Aires, el concepto es productos y la

moneda es ARS. Para servicios pasá service: { from, to, dueDate }.

  • ARCA acepta una fecha de hasta 5 días antes o después de hoy para productos,

sin pasar al mes siguiente, y 10 para servicios. preview() e issue() lanzan ArcaInputError con code: "ARCA_INPUT_DATE_OUTSIDE_WINDOW" y window: { from, to } fuera de esa ventana. voucherDateWindow() la calcula antes, sin I/O.

arca.preview(input) es sincrónico y no hace I/O. Devuelve la clase, el tipo, los importes y el pedido exacto. Compará preview(input).amounts.sentTotal con el total de la venta antes de llamar a issue().

Para campos menos comunes, como moneda extranjera, tributos, FCE o optionalFields, leé Emitir facturas.

Tratá los cuatro resultados

issue(), issueCreditNote(), issueDebitNote() y recover() devuelven el resultado fiscal en vez de lanzarlo. El código tiene que tratar los cuatro:

ts
switch (factura.kind) {
case "authorized":
// Guardá number, cae, caeExpiry, header, amounts y qr.
await guardar(factura.voucher);
break;
case "rejected":
// ARCA rechazó. El input corregido va con una clave nueva.
console.error(factura.issues);
break;
case "indeterminate":
// No se sabe si ARCA emitió. Conservá el número y conciliá.
console.error(factura.attempted, factura.lookup);
break;
case "conflict":
// Otro comprobante ocupa el número. Detené el flujo y avisá a una persona.
console.error(factura.attempted, factura.found);
break;
default:
factura satisfies never;
}

voucher.qr es la URL que codifica el QR del comprobante impreso y voucher.qrPayload su JSON. Para el PDF, no armes letra, leyendas ni IVA a mano: buildVoucherDocument({ voucher, items, issuer, saleConditions }) deriva todo lo que el comprobante impreso tiene que mostrar, con los mismos items de la emisión. Está en Comprobante impreso. Lo que sí se lanza son los errores de input (ArcaInputError, con un code como ARCA_INPUT_INVALID_AMOUNT) y de configuración (ArcaConfigurationError), siempre antes de escribir en ARCA. Un ArcaInputError significa que ARCA no emitió nada por esa llamada: corregí el input y reintentá.

Reintentos sin duplicados

ARCA no acepta una clave de idempotencia. Si la respuesta se pierde, reintentar a ciegas puede emitir dos facturas. Para producción, agregá un store persistente y pasá el ID estable de la venta como idempotencyKey:

ts
import { createArcaClient, createPostgresStore } from "facturas";
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const arca = createArcaClient({
store: createPostgresStore({ query: (text, params) => pool.query(text, params) }),
});
const factura = await arca.issue(input, { idempotencyKey: venta.id });

La tabla se crea una vez:

sql
CREATE TABLE arca_store (
key text PRIMARY KEY,
value text NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);

También hay createRedisStore(redis), createFileStore(dir) para un único servidor y createMemoryStore(), que solo sirve para pruebas porque no sobrevive a un reinicio. Reglas de la clave:

  • Una clave por operación de negocio, de 1 a 255 caracteres, sin CUIT, DNI ni

datos personales. Nunca un UUID nuevo por intento.

  • Una idempotencyKey sin store lanza antes de cualquier I/O.
  • Mismo input con la misma clave. Otro input lanza

ARCA_INPUT_IDEMPOTENCY_MISMATCH.

  • Después de rejected, el input corregido va con una clave nueva.
  • Nunca borres ni hagas vencer los registros del store: guardan el número que

un reintento tiene que consultar.

Ante indeterminate, recover(clave) consulta la reserva y nunca emite. Hacé lo que dice lookup.kind:

lookup.kindQué hacer
not_foundARCA confirmó que el número está vacío. Llamá otra vez al método original con la misma clave y el mismo input
failed, abortedLa consulta no terminó. Esperá y volvé a llamar a recover() o repetí la llamada original con la misma clave
incompleteARCA tiene un comprobante en el número reservado, pero la consulta no trae los datos para probar que es este. No emitas otro. Probá recover() una vez más y, si sigue igual, que lo revise una persona con reason y attempted
blockedOtra reserva frena la secuencia. Conciliá primero la clave by con recover() desde el emisor byTaxId y después repetí
supersededLa secuencia siguió sin esta clave. Emití con una clave nueva

recover() sobre una clave sin reserva lanza ARCA_INPUT_RESERVATION_NOT_FOUND. Detalles en Evitar comprobantes duplicados.

Notas de crédito y débito

ARCA no anula comprobantes. Una corrección es una nota de crédito, que también es un documento fiscal real. Nombrá la factura original con for y elegí el modo de forma explícita:

ts
const nota = await arca.issueCreditNote(
{
for: { salesPoint: 3, voucherType: 11, number: 41 }, // o factura.voucher
items: [{ amount: 50_000 }], // devolución parcial, ARS 500,00
},
{ idempotencyKey: `nc:${devolucion.id}` },
);
  • items acredita líneas y es el caso habitual. all: true acredita el

original completo. Uno de los dos, o un desglose amounts, es obligatorio.

  • La forma de los ítems sigue la clase del original: { amount } en C,

{ gross | net, vat } en A y B.

  • La clase, el receptor, la moneda y el concepto salen del original. La nota

no lleva issuer ni currency. Si la consulta del original no informa la condición de IVA del receptor, pasá to.condition. Si ya la informa, podés omitir to o aportar la misma condición. to solo admite condition. El tipo y el número de documento del receptor siempre salen del original.

  • issueDebitNote() tiene el mismo contrato sin all: true.
  • previewCreditNote() y previewDebitNote() son asincrónicas porque

consultan el original.

  • arca.lookup({ salesPoint, voucherType, number }) consulta un comprobante

autorizado, en centavos y fechas YYYY-MM-DD. Devuelve null si ARCA no lo tiene.

  • arca.lastAuthorized({ salesPoint, voucherType }) devuelve el último número

autorizado, o 0, en WSFE o WSMTXCA.

  • associatedPeriod: { from, to } en lugar de for ajusta un período. Es la

alternativa cuando no hay un comprobante puntual.

Detalles en Notas de crédito y débito.

Producción

  • Homologación y producción tienen certificados y puntos de venta propios. El

par de producción se genera con npx facturas init --cuit 20123456786 --env production.

  • ARCA_ENVIRONMENT=production emite documentos reales. Una prueba de ARS 1

también lo es y se corrige con una nota de crédito. Usá items: [{ amount: 100 }] en clase C y items: [{ gross: 100, vat: 21 }] en A y B para una operación con IVA del 21%.

  • Store persistente compartido por todos los procesos que emiten, e

idempotencyKey en cada emisión.

  • abortSignal: AbortSignal.timeout(20_000) evita llamadas colgadas. Un corte

después del envío devuelve indeterminate con lookup.kind === "aborted". Un corte mientras espera el lock de la secuencia lanza ArcaLockTimeoutError sin reservar nada: repetí la llamada.

  • Los logs quedan en warn. En debug se loguean los pedidos SOAP con datos

de clientes. Nunca loguees los PEM.

La lista completa está en Pasar a producción.

Qué evitar

  • Armar SOAP o XML a mano, o sumar otra librería de ARCA en paralelo.
  • Usar client.wsfe para emitir. Es el módulo de transporte. La emisión va por

issue(), issueCreditNote() e issueDebitNote().

  • Calcular el IVA, la clase o el tipo de comprobante a mano.
  • Importes en pesos con decimales en lugar de centavos.
  • Reintentar una emisión sin store ni idempotencyKey.
  • Pasar service: "wsmtxca" en las opciones sin que el contribuyente o el

punto de venta lo requiera. WSFE es el valor por defecto.

  • Probar en producción antes de homologación.

Documentación en Markdown

← v1.0.0All versions