# API de Facturación Electrónica ARCA — NanoFactura
Referencia completa para integrar facturación electrónica de ARCA (ex AFIP) por API REST: autenticación, emisión de comprobantes, equivalencia de campos con ARCA, tablas de códigos y manejo de errores.
Documentación completa en texto plano. Sitio: https://asistentefacturaelectronica.com/ayuda/
Generado automáticamente desde el código fuente de la documentación.
---
# Autenticación con API Key
> Cómo autenticar contra la API de facturación electrónica de NanoFactura con un token Bearer. Entornos de homologación y producción, alcance y permisos de las API Keys.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/autenticacion
La API Key viaja en la cabecera `Authorization` con el esquema `Bearer`:
```http
Authorization: Bearer PROD_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
```
Es el mismo esquema que usan OpenAI, GitHub o Stripe, así que cualquier cliente HTTP ya sabe mandarlo. La palabra `Bearer` es opcional —`Authorization: PROD_a1b2…` también funciona—, pero usala igual: es la forma que entienden los interceptores y las librerías de terceros sin configuración extra.
Las claves se crean desde la app, en **Administrar Empresa → API Keys**, y se muestran **una sola vez**. Ver [cómo crearlas](https://asistentefacturaelectronica.com/ayuda/api/api-keys).
## Entornos: el prefijo manda
Son 37 caracteres: un prefijo más 32 hexadecimales. **El prefijo es lo único que decide contra qué ARCA se opera.**
| Prefijo | Entorno ARCA | Certificado que usa | Validez fiscal |
| :--- | :--- | :--- | :---: |
| `TEST_` | Homologación | El activo de homologación | No |
| `PROD_` | Producción | El activo de producción | **Sí** |
Esto tiene consecuencias prácticas:
- **Los listados están separados.** Una clave `TEST_` nunca ve comprobantes de producción, ni al revés.
- **Cada entorno necesita su propio certificado activo** y sus los puntos de venta son los mismos para ambos, previamente habilitados en ARCA.
Podés ensayar en homologación contra la **misma empresa** con la que después vas a facturar de verdad, incluso reusando las mismas claves de idempotencia: los dos entornos llevan historias independientes y no se pisan.
## Qué habilita una API Key
Una clave opera **sólo sobre la empresa que aparece en la URL** con la que se la usa, y sólo sobre los recursos de facturación:
| Recurso | Ruta | |
| :--- | :--- | :--- |
| Comprobantes | `/:id-empresa/invoices/…` | ✅ |
| Consultas a ARCA | `/:id-empresa/arca/…` | ✅ |
| Puntos de venta | `/:id-empresa/pos/…` | ✅ |
| Datos de la empresa | `/:id-empresa/details` | ✅ |
| Verificación de la clave | `/:id-empresa/api-keys/test` | ✅ |
| Bitácora de la empresa | `/:id-empresa/audit-log` | ✅ |
| Suscripciones, pagos, miembros, certificados | — | ❌ `401` |
La administración de la cuenta queda deliberadamente **fuera** de la API: suscripciones y pagos, invitaciones y miembros, certificados, y la creación y revocación de las propias API Keys. Todo eso se hace desde la app, con tu usuario administrador. Una clave filtrada puede facturar, pero no puede cambiar quién accede a la cuenta ni tocar el certificado.
## Alcance dentro de esa empresa
Todas las claves se crean con **Acceso Total**: no hay niveles de permiso intermedios. Cualquier clave puede usar cualquiera de los endpoints de la tabla de arriba.
Por eso la separación se hace con **una clave por sistema**, no con permisos: cada comprobante guarda con qué clave se emitió, y revocar una no afecta a las demás.
## Buenas prácticas
- **Nunca la pongas en el código.** Va en una variable de entorno o en el gestor de secretos de tu plataforma. Si se filtró en un commit, revocala: rotarla es gratis e inmediato.
- **Nunca la uses desde el navegador ni desde una app móvil.** Cualquiera puede leerla del bundle o interceptarla. La API se llama **desde tu servidor**.
- **Una clave por sistema.** Si tenés un ERP y una tienda online, dales claves distintas: cuando algo falle vas a saber cuál fue, y podés revocar una sin frenar la otra. Cada comprobante guarda con qué clave se emitió.
- **Revocá las que no uses.** La revocación es inmediata.
Si perdés una clave no hay forma de recuperarla: se guardan hasheadas y sólo se muestran al crearlas. Revocala y creá otra.
## Verificar que funciona
```bash
curl https://nanofactura.com/api/mi-empresa/api-keys/test \
-H "Authorization: Bearer TEST_tu_api_key"
```
```json
{ "ok": true, "environment": "homologation", "certificates": true, "pos": true }
```
| Campo | Qué significa |
| :--- | :--- |
| `environment` | `"production"` o `"homologation"`, derivado del prefijo. |
| `certificates` | `true` si hay un certificado activo para ese entorno. |
| `pos` | `true` si hay al menos un punto de venta que sirva para facturar por API. |
Un `401` significa clave inválida, revocada, mal copiada, o usada contra un `id-empresa` que no le corresponde.
> **Es para probar, no para llamar siempre**
>
> Usalo al integrar, al pasar a producción y cuando algo falla. **No lo llames antes de cada emisión**: lo que verifica —certificado y puntos de venta— cambia una vez cada dos años, así que un chequeo por comprobante duplica la latencia y no evita ningún error. Si falta configuración, la emisión lo dice igual.
---
# Equivalencia de campos con ARCA y obligatoriedad
> Diccionario campo por campo entre la API de NanoFactura y los campos de ARCA (FECAESolicitar), con qué es obligatorio según el tipo de comprobante y la condición fiscal del emisor y del receptor.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/campos-arca
Esta página responde dos preguntas: **cómo se llama en ARCA cada campo de la API**, y **cuándo es obligatorio** según el comprobante que emitas y quién sea tu receptor.
Si venís de una integración directa contra ARCA, es el diccionario que necesitás para traducir lo que ya tenías.
## Qué comprobante te corresponde
Lo determina **tu** condición fiscal como emisor:
| Tu condición fiscal | Comprobantes que emitís | Cuándo |
| :--- | :--- | :--- |
| **Responsable Monotributo** | **C** — `11` `12` `13` | Siempre, a cualquier receptor |
| **IVA Sujeto Exento** | **C** — `11` `12` `13` | Siempre, a cualquier receptor |
| **IVA Responsable Inscripto** | **A** — `1` `2` `3` | Si el receptor es Responsable Inscripto **o Monotributista** |
| | **B** — `6` `7` `8` | Si el receptor es consumidor final o exento |
Es la regla que más consultas genera: **si sos monotributista emitís C y no discriminás IVA**, sin importar a quién le factures. Ver [Tipos de comprobante](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante).
## Matriz de obligatoriedad
**●** obligatorio · **◐** condicional · **○** se acepta pero se ignora
| Campo (NanoFactura) | Campo (ARCA) | A | B | C | Cuándo es obligatorio |
| :--- | :--- | :-: | :-: | :-: | :--- |
| `idempotency` | — | ● | ● | ● | Siempre. Es de NanoFactura, no de ARCA. |
| `posNumber` | `PtoVta` | ● | ● | ● | Siempre. |
| `voucherType` | `CbteTipo` | ● | ● | ● | Siempre. |
| `concept` | `Concepto` | ● | ● | ● | Siempre. |
| `voucherDate` | `CbteFch` | ● | ● | ● | Siempre. |
| `customer.docType` | `DocTipo` | ● | ● | ● | Siempre. En **A** tiene que ser CUIT (`80`). |
| `customer.docNumber` | `DocNro` | ● | ◐ | ◐ | Siempre presente. En **B** y **C** se admite `99`/`0` sólo por debajo del importe que fija ARCA. |
| `customer.name` | — | ● | ● | ● | Siempre. Se imprime en el comprobante. |
| `customer.taxType` | `CondicionIVAReceptorId` | ● | ● | ● | Siempre. En **A** tiene que ser Responsable Inscripto (`1`) o Monotributo (`6`). |
| `customer.address` | — | ○ | ○ | ○ | Nunca, pero recomendado: se imprime en el PDF. |
| `customer.stateId` | — | ○ | ○ | ○ | Nunca, pero **recomendado**: ver [nota](#por-qué-conviene-mandar-la-provincia). |
| `items[]` | — | ● | ● | ● | Siempre, mínimo una línea. |
| `items[].taxType` | `Iva[].Id` | ● | ● | ○ | En **A** y **B** siempre. **En C no va**: no se discrimina IVA. |
| `serviceFrom` | `FchServDesde` | ◐ | ◐ | ◐ | Si `concept` es `2` o `3`. |
| `serviceTo` | `FchServHasta` | ◐ | ◐ | ◐ | Si `concept` es `2` o `3`. |
| `serviceExpiration` | `FchVtoPago` | ◐ | ◐ | ◐ | Si `concept` es `2` o `3`. |
| `associatedVouchers` | `CbtesAsoc` | ◐ | ◐ | ◐ | En notas de crédito y débito. |
| `currency.id` | `MonId` | ○ | ○ | ○ | Nunca. Por defecto `PES`. |
| `currency.quotation` | `MonCotiz` | ◐ | ◐ | ◐ | Si `currency.id` no es `PES`. |
| `cancelsInSameForeignCurrency` | `CanMisMonExt` | ◐ | ◐ | ◐ | Sólo en moneda extranjera. |
| `tributes[]` | `Tributos` | ○ | ○ | ○ | Nunca. Sólo si corresponden percepciones o impuestos que no son IVA. |
| `receiverTaxConditionId` | `CondicionIVAReceptorId` | ○ | ○ | ○ | Nunca. Si se envía, pisa a `customer.taxType`. |
| `emails`, `legend`, `comments`, `externalReference`, `additionalData`, `settings` | — | ○ | ○ | ○ | Nunca. Son de NanoFactura, no viajan a ARCA. |
### Las cuatro reglas condicionales
Son las que causan casi todos los rechazos:
1. **Servicios → tres fechas.** Si `concept` es `2` (servicios) o `3` (productos y servicios), **las tres** fechas de servicio son obligatorias. Faltando cualquiera, la API responde `400` antes de llamar a ARCA, indicando cuál falta.
2. **Nota de crédito o débito → comprobante asociado.** Y **de la misma letra**: una nota de crédito C anula una factura C.
3. **Factura A → receptor identificado con CUIT.** `docType` tiene que ser `80`, y el receptor tiene que ser Responsable Inscripto (`1`) o Monotributista (`6`). Si le facturás a un consumidor final o a un exento, el comprobante que corresponde es **B**.
4. **Moneda extranjera → cotización.** Si `currency.id` no es `PES`, mandá `currency.quotation` con la cotización del día. No se consulta sola.
### Identificación del receptor en B y C
En **Factura B y C** podés facturar a consumidor final sin identificarlo (`docType: 99`, `docNumber: "0"`) **sólo por debajo del importe que fija ARCA**. Por encima de ese monto hay que identificar al comprador con DNI o CUIT.
ARCA actualiza ese umbral periódicamente. No lo hardcodees: si te pasás, el rechazo llega con el código **`10015`** y un mensaje explícito.
```json
{
"ok": false,
"error": "(10015) Factura B con importe total mayor a $ … requiere identificación del comprador",
"code": "ARCA_ERROR",
"details": [{ "code": 10015, "msg": "…" }]
}
```
La forma correcta de manejarlo: **pedí siempre el documento si lo tenés**, y tratá el `10015` como "pedile los datos al cliente y reintentá".
## Lo que la API completa sola
Estos campos de ARCA no se envían: los calcula o los lleva la API.
| Campo ARCA | De dónde sale |
| :--- | :--- |
| `Cuit` | El CUIT de tu empresa. |
| `CbteDesde` / `CbteHasta` | Numeración correlativa por punto de venta y tipo. **No lleves contadores.** |
| `ImpNeto` | Suma de las líneas gravadas (y de **todas** en comprobantes C). |
| `ImpIVA` | Suma del IVA calculado por línea. |
| `ImpOpEx` | Suma de las líneas exentas o al 0% (`taxType` `2` y `3`). |
| `ImpTotConc` | Suma de las líneas no gravadas (`taxType` `1` o desconocido). |
| `ImpTrib` | Suma de los `tributes[].subtotal`. |
| `ImpTotal` | `ImpNeto + ImpIVA + ImpTrib + ImpOpEx + ImpTotConc`. |
| `Iva[]` | Un `{ Id, BaseImp, Importe }` por cada alícuota gravada presente en los ítems. |
| `Tributos[]` | Un `{ Id, Desc, BaseImp, Alic, Importe }` por cada `tributes[]`. |
| Token y firma | Autenticación con ARCA, renovada automáticamente. |
| `CAE` / `CAEFchVto` | Los devuelve ARCA y quedan en la respuesta. |
> **¿Por qué no se mandan los importes?**
>
> Los totales que ARCA acepta tienen reglas de redondeo estrictas y consistentes entre sí: si `ImpTotal` no coincide exactamente con la suma de sus partes, el comprobante se rechaza. Calcularlos de nuestro lado en la API en base a la información que envías de cantidad, precio y alícuota elimina toda una categoría de rechazos por centavos.
## Por qué conviene mandar la provincia
`customer.stateId` **no se le envía a ARCA** —el domicilio que se imprime es `address`, en texto libre— pero es el único dato **estructurado** de ubicación que queda guardado con el comprobante.
Con la provincia cargada podés después:
- Saber **cómo se reparte tu facturación por jurisdicción**, que es la base de cualquier liquidación de **Ingresos Brutos** y del **Convenio Multilateral**.
- Filtrar y agrupar tus reportes por provincia sin trabajo manual.
- Hacer que el PDF imprima las **leyendas provinciales** que correspondan (defensa al consumidor, IIBB local). Sin `stateId`, esas leyendas no aparecen.
`address` es texto libre y no se puede agregar ni agrupar de forma confiable. **Y no hay forma de reconstruir la provincia hacia atrás**: si no la mandaste al emitir, ese dato no existe más.
Mandá `null` cuando genuinamente no la sepas — es un valor válido que significa "no informada", y es mejor que inventar `0` (CABA), que te ensucia los reportes con facturación que no es de CABA. Podés reenviar tal cual el `stateId` de la [consulta de padrón](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca).
## Glosario rápido
Si venís de una integración directa, la traducción de vocabulario:
| En ARCA | En NanoFactura |
| :--- | :--- |
| `FECAESolicitar` | `POST /:id-empresa/invoices` |
| `FECompUltimoAutorizado` | No hace falta: la numeración la lleva la API |
| `FECompConsultar` | No hace falta: reintentá con la misma `idempotency` |
| `FEParamGetCondicionIvaReceptor` | `GET /:id-empresa/arca/condicion-iva-receptor` |
| `FEDummy` | `GET /:id-empresa/arca/dummy` |
| Padrón A13 / Constancia de Inscripción | `GET /:id-empresa/arca/padron/:docNumber` |
| `FEParamGetPtosVenta` | `GET /:id-empresa/pos/arca` |
| Autenticación, token, firma, certificado | Automático: sólo mandás la API Key |
Todas las tablas de códigos de ARCA, tal como las usa la API: **[Tablas de códigos](https://asistentefacturaelectronica.com/ayuda/api/tablas/tipos-de-comprobante)**.
---
# Crear y administrar API Keys
> Cómo crear la API Key para integrar la facturación electrónica de ARCA, elegir entre homologación y producción, asignar permisos y revocarla si se filtra.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/api-keys
La API Key es la credencial con la que tu sistema factura. Se crean y se revocan **desde la app**, en **Administrar Empresa → API Keys**.
## Crear una
En **Administrar Empresa → API Keys → Nueva clave** elegís dos cosas:
| | |
| :--- | :--- |
| **Descripción** | Para qué es. Poné el nombre del sistema: `ERP producción`, `Tienda online`. Cuando tengas varias, es lo único que las distingue. |
| **Entorno** | `TEST_` (homologación, sin validez fiscal) o `PROD_` (producción, fiscal). |
> **La clave se muestra una sola vez**
>
> **no hay forma de recuperarla después**. Copiala en ese momento y guardala en tu gestor de secretos o en la variable de entorno. Si la perdés, revocala y creá otra.
Después de crearla, en el listado sólo vas a ver una versión enmascarada (`PROD_••••••••5f9e`), suficiente para identificar cuál es cuál.
## Elegir el entorno
**Empezá siempre por `TEST_`.** Los comprobantes de homologación no tienen validez fiscal y podés equivocarte todo lo que haga falta.
| Prefijo | Entorno ARCA | Validez fiscal |
| :--- | :--- | :---: |
| `TEST_` | Homologación | No |
| `PROD_` | Producción | **Sí** |
El prefijo es lo único que decide el entorno: **no hay ningún parámetro** que lo cambie desde el pedido. Pasar a producción es cambiar el valor de una variable de entorno, sin tocar el código.
Cada entorno necesita su propio [certificado activo](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca) y sus puntos de venta habilitados en ARCA.
## Alcance
Todas las API Keys se crean con **Acceso Total**: pueden usar cualquiera de los endpoints habilitados para claves —emitir y consultar comprobantes, administrar puntos de venta, consultar ARCA y leer la bitácora—. No hay niveles de permiso intermedios.
Lo que **ninguna** clave puede hacer, por diseño: tocar el certificado, cambiar la suscripción, invitar usuarios ni crear otras claves. Eso se hace desde la app, con tu usuario. Ver [alcance de las API Keys](https://asistentefacturaelectronica.com/ayuda/api/autenticacion#qué-habilita-una-api-key).
Como toda clave tiene acceso total, la separación se hace **creando una clave por sistema** y revocando la que sobre.
## Buenas prácticas
- **Una clave por sistema.** Si tenés un ERP y una tienda online, dales claves distintas: cada comprobante guarda con cuál se emitió, así que cuando algo falle vas a saber de dónde vino, y podés revocar una sin frenar la otra.
- **Nunca en el código ni en el repositorio.** Va en una variable de entorno o en el gestor de secretos de tu plataforma.
- **Nunca en el navegador ni en una app móvil.** Cualquiera puede leerla del bundle o interceptarla. La API se llama **desde tu servidor**.
- **Revocá las que no uses.**
```bash title=".env — nunca lo subas al repositorio"
NANOFACTURA_API_KEY=TEST_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
NANOFACTURA_ID_EMPRESA=mi-empresa
```
## Si se filtró
**Revocala.** Desde **Administrar Empresa → API Keys**, el efecto es inmediato: la próxima petición con esa clave responde `401`.
Lo que la clave filtrada **no** pudo hacer, por diseño: tocar tu certificado, cambiar la suscripción, invitar usuarios, ni crear otras claves. Todo eso está fuera del alcance de una API Key.
Lo que **sí** pudo hacer es emitir comprobantes. Revisá el listado filtrando por esa clave:
```http
GET /:id-empresa/invoices?apiKeyId=3&ordering=-created
```
Si hay comprobantes que no reconocés, hay que emitir las notas de crédito correspondientes ([cómo](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante#notas-de-crédito-y-débito)). Escribinos a [contacto@nanofactura.com](mailto:contacto@nanofactura.com) si necesitás ayuda.
También queda registro en la bitácora de la empresa, con qué se hizo, cuándo y con qué clave:
```http
GET /:id-empresa/audit-log
```
## Verificar una clave
```bash
curl https://nanofactura.com/api/mi-empresa/api-keys/test \
-H "Authorization: Bearer TEST_tu_api_key"
```
```json
{ "ok": true, "environment": "homologation", "certificates": true, "pos": true }
```
Es la primera llamada que conviene hacer al integrar: valida la clave y confirma que la empresa tenga certificado y puntos de venta para ese entorno. Un `401` significa clave inválida, revocada, mal copiada o usada contra otro `id-empresa`.
## Y ahora
Ya tenés todo. Andá a **[Primeros pasos](https://asistentefacturaelectronica.com/ayuda/api/primeros-pasos)** y emití tu primera factura.
---
# Vincular tu certificado digital de ARCA
> Cómo generar el pedido de certificado, tramitarlo en el portal de ARCA y delegar la facturación electrónica. Paso previo obligatorio para emitir comprobantes por API.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/certificado-arca
El certificado digital es lo que le permite a NanoFactura emitir comprobantes **en tu nombre** ante ARCA. Sin él no se puede facturar.
Es un trámite de **una sola vez** por entorno, y el certificado tiene validez de dos años.
Esta página cubre el certificado de **producción**, el que emite comprobantes con validez fiscal. Sirve tanto para facturar desde la aplicación como para integrar con la API. Si querés probar primero contra el entorno de pruebas, mirá [Certificado de homologación](https://asistentefacturaelectronica.com/ayuda/api/certificado-homologacion).
## Video: configurar el certificado en 4 pasos
## Antes de empezar
Necesitás:
- **Clave Fiscal nivel 3** o superior.
- El servicio **Administración de Certificados Digitales** habilitado en tu Clave Fiscal.
- Ser el titular del CUIT, o tener la delegación correspondiente.
- Una **empresa Premium** en la aplicación.
## Paso 1 — Creá la solicitud de certificado
En la aplicación, seleccioná la empresa Premium y entrá a **Administrar → Certificados → Agregar**.
1. Escribí un **alias** descriptivo (por ejemplo `nanofacturaprod`). Anotalo: lo vas a volver a escribir *exactamente igual* en ARCA.
2. Activá la opción **Producción**, para emitir comprobantes con validez fiscal.
3. **Descargá el archivo de solicitud** (CSR). Queda en la carpeta de descargas del navegador.
## Paso 2 — Creá el certificado en ARCA
1. Entrá a **[arca.gob.ar](https://www.arca.gob.ar)** con tu Clave Fiscal y abrí el servicio **Administración de Certificados Digitales**.
2. Seleccioná el **CUIT** para el que vas a facturar.
3. En **Alias**, escribí exactamente el mismo nombre que usaste en la aplicación.
4. Subí el archivo de solicitud que descargaste y hacé clic en **Agregar alias**.
5. Con el certificado creado, entrá a **Ver** y **descargá el archivo generado** (`.crt`).
## Paso 3 — Delegá los servicios
Este es el paso que más se olvida. Tener el certificado no alcanza: hay que asociarlo al servicio de facturación.
1. Iniciá sesión nuevamente en ARCA y entrá a **Administrador de Relaciones de Clave Fiscal**.
2. Hacé clic en **Adherir servicio**.
3. Buscá la sección de **ARCA**, desplegá **Web Services** y elegí **Facturación Electrónica**.
4. En el campo **Representante**, hacé clic en **Buscar**, elegí como *computador fiscal* el certificado que acabás de crear y confirmá.
5. Confirmá otra vez para cerrar la vinculación.
> **El error típico**
>
> Si el certificado está bien pero falta este paso, ARCA rechaza la autenticación aunque todo *parezca* configurado. Es la causa número uno de "tengo el certificado y no puedo facturar".
>
> Se reconoce por el mensaje: **`Computador no autorizado a acceder al servicio`**. Ver [Errores](https://asistentefacturaelectronica.com/ayuda/api/errores#502-arca_connection_error--computador-no-autorizado-a-acceder-al-servicio).
### Si vas a consultar el padrón
Cada servicio de ARCA se delega **por separado**. Si además de facturar vas a usar la [consulta de padrón](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca) —lo recomendado para armar el cliente con la condición fiscal real— repetí el paso 3 con el mismo certificado para *Consulta a Padrón A13* y *Constancia de Inscripción*.
No son obligatorios para facturar: sin esa delegación la emisión funciona igual, pero la consulta de padrón falla con el mismo `Computador no autorizado a acceder al servicio`.
## Paso 4 — Cargá y probá el certificado
1. Volvé a la aplicación y hacé clic en **Cargar certificado**.
2. Seleccioná el archivo que descargaste de ARCA.
3. Una vez validado, pulsá **Probar**. A los pocos segundos deberías ver **OK** en todos los servicios delegados.
4. Aceptá y **activá** el certificado.
Podés tener varios certificados cargados, pero **un solo certificado activo por entorno**.
¡Listo! Con eso ya podés facturar.
## Renovación
Los certificados de ARCA duran **dos años**. Cuando se acerca el vencimiento repetís el mismo trámite: generás una solicitud nueva, la subís a ARCA y cargás el `.crt` resultante.
Te avisamos por email antes de que venza. **Tu código no cambia** al renovar: la API Key sigue siendo la misma.
## Y ahora
Con el certificado activo, seguí con **[los puntos de venta](https://asistentefacturaelectronica.com/ayuda/api/puntos-de-venta)**.
---
# Certificado de homologación (entorno de pruebas)
> Cómo generar y vincular el certificado de homologación de ARCA para probar la facturación y la integración sin emitir comprobantes con validez fiscal.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/certificado-homologacion
**Homologación** es el entorno de pruebas de ARCA: los comprobantes que emitís ahí **no tienen validez fiscal** y sirven para probar la facturación o la integración antes de salir a producción.
> **Son dos certificados distintos**
>
> El certificado de homologación y el de producción se tramitan por separado, en servicios distintos de ARCA, y no son intercambiables. Si vas a probar antes de salir a producción —lo recomendado— vas a hacer el trámite dos veces.
>
> Para el de producción, ver [Vincular tu certificado de ARCA](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca).
## Video: configurar el certificado de prueba
## Paso 1 — Creá la solicitud de certificado
En la aplicación, seleccioná tu empresa Premium y entrá a **Administrar → Certificados → Agregar**.
1. Escribí un **alias** descriptivo que te ayude a identificarlo (por ejemplo `nanofacturahomo`). Lo vas a volver a escribir *exactamente igual* en ARCA.
2. Asegurate de que la opción **Producción** **no** esté seleccionada.
3. Hacé clic en **Generar solicitud de certificado** y **copiá** la solicitud con el botón.
A diferencia de producción, acá la solicitud se copia al portapapeles: en homologación ARCA la pide pegada, no como archivo.
## Paso 2 — Creá el certificado en ARCA
1. Iniciá sesión en ARCA y buscá el servicio **Autogestión de Certificados de Homologación**.
2. Elegí crear un **nuevo certificado**.
3. En **Nombre simbólico** escribí el mismo alias que usaste en la aplicación.
4. Hacé clic derecho en el campo de la solicitud y elegí **Pegar**.
5. Hacé clic en el botón para **obtener el certificado**.
6. Seleccioná todo el contenido del certificado, clic derecho sobre la selección y **Copiar**.
## Paso 3 — Guardá el certificado en la aplicación
Volvé a la aplicación, hacé clic derecho en el campo del certificado, elegí **Pegar** y cargalo con el botón. Se hace una validación en el momento; después volvemos a ARCA para terminar de configurarlo.
## Paso 4 — Delegá los servicios
De vuelta en ARCA, dentro del mismo servicio de homologación:
1. Entrá a la sección de **servicios**, buscá **`wsfe`** (Facturación Electrónica) y hacé clic en **Ver**.
2. En **acciones posibles**, elegí **crear autorización para acceder a este servicio**.
3. Seleccioná el **certificado** que acabás de crear.
4. Corroborá que el **CUIT representado** sea el de la empresa con la que vas a facturar.
5. Confirmá que el **servicio** sea el que estás delegando y hacé clic en **crear autorización de acceso**.
Vas a ver que el resultado de la asociación fue exitoso.
### Servicios opcionales
Si además vas a probar la [consulta de padrón](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca), repetí el paso 4 con el mismo certificado para los otros dos servicios. No son obligatorios para facturar.
En homologación los servicios figuran con su nombre técnico:
| Servicio | Para qué |
| --- | --- |
| `wsfe` | Emitir comprobantes. **Obligatorio.** |
| `ws_sr_padron_a13` | Consultar datos y condición fiscal por DNI/CUIL. Opcional. |
| `ws_sr_constancia_inscripcion` | Consultar datos y condición fiscal por CUIT. Opcional. |
## Paso 5 — Probá y activá
En la aplicación, hacé clic en **Probar**. A los pocos segundos vas a ver el estado de cada servicio configurado —los dos últimos son los opcionales—. Cuando la configuración sea exitosa, **Aceptá** y asegurate de dejar **activo** el certificado que acabás de crear.
> **Si algún servicio falla, probá de nuevo**
>
> El servidor de homologación de ARCA es inestable y falla de a ratos sin que haya nada mal configurado. Antes de revisar el trámite, repetí la prueba.
¡Listo! Ya podés facturar en modo prueba.
Podés tener activo un certificado de homologación y uno de producción al mismo tiempo: son entornos independientes.
## Verificá desde la API
```bash
curl https://nanofactura.com/api/mi-empresa/api-keys/test \
-H "Authorization: Bearer TEST_tu_api_key"
```
```json
{ "ok": true, "environment": "homologation", "certificates": true, "pos": true }
```
Las API Keys de prueba empiezan con `TEST_` y siempre operan contra homologación. Ver [API Keys](https://asistentefacturaelectronica.com/ayuda/api/api-keys).
## Pasar a producción
Cuando la integración funciona en homologación, el salto a producción es tramitar el certificado de producción y cambiar la API Key. **Tu código no cambia.** Seguí con [Vincular tu certificado de ARCA](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca).
---
# Configurar puntos de venta para la API
> Cómo dar de alta un punto de venta en ARCA y en NanoFactura para emitir facturas electrónicas por API. Endpoints de consulta, alta, modificación y carga de logo.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/puntos-de-venta
Un punto de venta es la numeración bajo la cual emitís ejemplo 00001. Cada uno lleva su propia secuencia correlativa por tipo de comprobante, y es el valor que mandás como `posNumber` al [emitir con API](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante).
Tiene que existir **de los dos lados**: dado de alta en ARCA y configurado en NanoFactura.
## Paso 1 — Dalo de alta en ARCA
En el portal de ARCA, con tu Clave Fiscal:
1. Entrá a **Administración de Puntos de Venta y Domicilios**.
2. **Agregar punto de venta**.
3. Elegí el sistema **Factura Electrónica ó Servicio Web** (RECE).
> **El tipo de punto de venta importa**
>
> Un punto de venta creado para **Comprobantes en Línea** (el portal web de ARCA) **no sirve para facturar por API**. Son numeraciones distintas y ARCA las trata por separado. Tiene que ser de **servicio web**.
>
> Si vas a usar los dos canales, necesitás un punto de venta de cada tipo.
El entorno de **homologación** usa los mismos puntos de venta que producción.
## Paso 2 — Verificá que ARCA lo tenga
```http
GET /:id-empresa/pos/arca
```
Consulta a ARCA los puntos de venta efectivamente habilitados para facturación electrónica en el entorno de tu clave. Es la forma de confirmar que el número que vas a usar existe del lado de ARCA antes de intentar emitir.
## Paso 3 — Configuralo en NanoFactura
Podés hacerlo desde la app (**Administrar Empresa → Puntos de venta**) o por API:
```bash
curl -X POST https://nanofactura.com/api/mi-empresa/pos \
-H "Authorization: Bearer PROD_tu_api_key" \
-H "Content-Type: application/json" \
-d '{
"number": 1,
"name": "Casa Central",
"address": "Av. de Mayo 500, CABA",
"email": "ventas@example.com",
"legend": "Gracias por su compra"
}'
```
| Campo | Obligatorio | Descripción |
| :--- | :---: | :--- |
| `number` | **Sí** | El número del punto de venta, igual al de ARCA. Es el `posNumber` al emitir. |
| `name` | No | Nombre de fantasía. Se imprime en el comprobante. |
| `address` | No | Domicilio comercial. Se imprime en el comprobante. |
| `email` | No | Email de contacto que aparece en el comprobante. |
| `legend` | No | Leyenda fija al pie de todos los comprobantes de este punto de venta. |
| `webOnly` | No | `true` sólo para puntos de venta de *Comprobantes en Línea*. **No sirven para la API.** |
## Consultar los configurados
```http
GET /:id-empresa/pos
```
```json
{
"ok": true,
"result": [
{
"id": "-Oxf2…",
"number": 1,
"name": "Casa Central",
"address": "Av. de Mayo 500, CABA",
"email": "ventas@example.com",
"webOnly": false,
"legend": "",
"logoURL": "https://…",
"cuit": "20383738890",
"inicioActividades": "2015-03-01",
"ingresosBrutos": "20-38373889-0",
"opConRetenciones": false,
"condIVA": 6
}
]
}
```
`number` es lo que mandás como `posNumber`. Los campos fiscales (`cuit`, `inicioActividades`, `ingresosBrutos`, `condIVA`) vienen de los datos de la empresa y son los que se imprimen en el encabezado del comprobante.
> **`webOnly: true` no sirve para la API**
>
> Esos puntos de venta están pensados para comprobantes cargados a mano en el portal de ARCA. Tienen `number: null` y no se pueden usar como `posNumber`. Filtralos.
## Endpoints
| Método | Ruta | Descripción |
| :--- | :--- | :--- |
| `GET` | `/:id-empresa/pos` | Lista los configurados |
| `GET` | `/:id-empresa/pos/:posId` | Devuelve uno |
| `GET` | `/:id-empresa/pos/arca` | Lista los habilitados **en ARCA** |
| `POST` | `/:id-empresa/pos` | Crea uno |
| `PATCH` | `/:id-empresa/pos/:posId` | Modifica uno |
| `POST` | `/:id-empresa/pos/:posId/logo` | Sube el logo |
| `DELETE` | `/:id-empresa/pos/:posId` | Elimina uno |
## Logo del comprobante
```http
POST /:id-empresa/pos/:posId/logo
```
Se manda como `multipart/form-data` con el campo `logo`.
| Requisito | Valor |
| :--- | :--- |
| Formatos | **JPG o PNG** |
| Peso máximo | 100 KB |
| **Tamaño recomendado** | **400×185 px** |
El logo se imprime a 400×185 y **la API no redimensiona**: mandalo ya ajustado a esa proporción.
> **Desde la app es más fácil**
>
> La aplicación web normaliza el logo sola: lo recorta a 400×185 sobre fondo blanco y lo comprime. Si no querés programar el redimensionado, subilo desde ahí una vez y listo.
## Cuántos puedo tener
El tope es de **10 puntos de venta** por empresa. Ver [Límites](https://asistentefacturaelectronica.com/ayuda/api/limites).
## Y ahora
Con el punto de venta configurado, seguí con **[la API Key](https://asistentefacturaelectronica.com/ayuda/api/api-keys)**.
---
# Consultar comprobantes emitidos
> Cómo listar y filtrar las facturas electrónicas emitidas por API. Filtros por fecha, cliente, punto de venta, importe y referencia externa, con ordenamiento y paginado.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/consultar-comprobantes
```http
GET /:id-empresa/invoices
```
Devuelve los comprobantes **del entorno de tu API Key**, con filtros, ordenamiento y paginado. Una clave `TEST_` nunca ve comprobantes de producción, ni al revés.
```bash
curl "https://nanofactura.com/api/mi-empresa/invoices?date__gte=20260801&ordering=-date&limit=50" \
-H "Authorization: Bearer PROD_tu_api_key"
```
## Filtros
El estilo es `?campo=valor` o `?campo__operador=valor`, y todos se combinan con `AND`.
**Todos los campos aceptan todos los operadores.** Lo que cambia entre uno y otro no es qué podés pedir, sino **qué tan rápido responde**: algunos filtros van directo por índice y otros obligan a recorrer tus comprobantes. La columna *Búsqueda* lo dice.
| Campo | Tipo | Corresponde a | Búsqueda |
| :--- | :--- | :--- | :--- |
| `id` | Integer | Identificador interno | **Directa** |
| `cae` | String | CAE | **Directa** |
| `idempotency` | String | Clave de idempotencia | **Directa** |
| `externalReference` | String | **Tu** identificador del sistema externo | **Directa** |
| `date` | String | Fecha fiscal `YYYYMMDD` (`voucherDate`) | **Directa**, ideal por rango |
| `created` | String | Momento de la emisión, ISO 8601 | **Directa**, ideal por rango |
| `pos` | Integer | Punto de venta (`posNumber`) | **Directa** |
| `number` | Integer | Número de comprobante (`voucherNumber`) | Directa **si va con `pos`** |
| `docType` | Integer | Tipo de documento del receptor | **Directa** |
| `docNumber` | String | Documento del receptor | Directa **si va con `docType`** |
| `type` | Integer | Tipo de comprobante (`voucherType`) | Recorrido |
| `total` | Number | Importe total | Recorrido |
| `status` | String | `APPROVED` | Recorrido |
| `source` | String | `api`, `app` (app web) o `rcel` (Comprobantes en Línea de ARCA) | Recorrido |
| `apiKeyId` | Integer | API Key que lo emitió | Recorrido |
| `userEmail` | String | Usuario que lo emitió desde la app | Recorrido |
### Operadores
| Operador | Significado | Ejemplo |
| :--- | :--- | :--- |
| *(ninguno)* | Igual | `?status=APPROVED` |
| `__gt` | Mayor que | `?total__gt=1000` |
| `__gte` | Mayor o igual | `?date__gte=20260101` |
| `__lt` | Menor que | `?total__lt=50000` |
| `__lte` | Menor o igual | `?date__lte=20260131` |
| `__in` | Dentro de una lista, separada por comas | `?type__in=1,6,11` |
Cualquier otro sufijo (`__contains`, `__startswith`, `__ne`…) **no existe** y se ignora.
### Cómo consultar rápido
> **Para buscar por documento, mandá también `docType`**
>
> `?docNumber=35888999` funciona, pero recorre tus comprobantes. `?docType=96&docNumber=35888999` va directo.
>
> El motivo es que los dos campos se indexan **juntos, en ese orden**: sin el tipo de documento no hay por dónde entrar. Lo mismo pasa con `number`, que va indexado junto a `pos`: `?pos=1&number=45` es directo, `?number=45` solo no.
>
> En la práctica no cuesta nada: el `docType` lo tenés siempre, porque es un dato que ya mandaste al emitir.
**Combiná siempre un filtro de recorrido con uno directo.** Si querés "las notas de crédito de agosto", pedí `?date__gte=20260801&date__lte=20260831&type__in=3,8,13`: el rango de fechas acota primero y el tipo filtra sobre eso. Un `?type=13` solo tiene que mirar todos tus comprobantes.
> **Los campos desconocidos se ignoran en silencio**
>
> Un filtro mal escrito no da error: simplemente no filtra, y recibís más resultados de los que esperabas. Verificá los nombres contra esta tabla.
>
> Vale también para los valores: `?limit=abc` cae al valor por defecto en vez de fallar.
## Orden y paginado
| Parámetro | Por defecto | Descripción |
| :--- | :--- | :--- |
| `ordering` | `-id` | Campos separados por coma; el prefijo `-` invierte. Ej. `?ordering=-date,total` |
| `limit` | `50` | Máximo `200` |
| `offset` | `0` | Desplazamiento |
## Respuesta
```json
{
"ok": true,
"total": 1284,
"result": [
{
"id": 1485,
"voucherType": 11,
"posNumber": 1,
"voucherNumber": 45,
"voucherDate": "20260613",
"docType": 96,
"docNumber": "35888999",
"totalAmount": "142500",
"cae": "76251489623547",
"caeExpirationDate": "20260623",
"status": "APPROVED",
"production": true,
"currency": "PES",
"currencyQuotation": 1,
"externalReference": "EXT-ORD-99882",
"idempotency": "b6f0…",
"apiKeyId": 3,
"userEmail": null,
"source": "api",
"created": "2026-06-13T14:22:09.481Z",
"hasPdf": true,
"customer": { },
"items": [ ],
"vat": [ ],
"emails": [ ]
}
]
}
```
`total` es la cantidad que **matchea el filtro**, no la de la página: es lo que te permite paginar sin depender de que la página vuelva llena.
Cada fila trae el payload completo del comprobante (`customer`, `items`, `tributes`, `vat`, `emails`, `legend`, `additionalData`) desplegado al mismo nivel. `hasPdf` indica si la generación en segundo plano ya terminó.
> **`totalAmount` y `currencyQuotation` vienen como string**
>
> Son columnas numéricas de precisión fija y viajan como string para no perder decimales al serializarlas. **Convertilas antes de operar**: en JavaScript `suma + fila.totalAmount` concatena en vez de sumar, y en Python `sum(...)` corta con `TypeError`. Usá `Number(...)`, `float(...)` o `(float)`.
## Recetas
```http title="Buscar por tu propio identificador"
GET /:id-empresa/invoices?externalReference=EXT-ORD-99882
```
```http title="Facturación de un mes"
GET /:id-empresa/invoices?date__gte=20260801&date__lte=20260831&ordering=date
```
```http title="Todo lo emitido a un cliente (con docType, para que vaya directo)"
GET /:id-empresa/invoices?docType=96&docNumber=35888999&ordering=-date
```
```http title="Un comprobante puntual: el 0001-00000045"
GET /:id-empresa/invoices?pos=1&number=45
```
```http title="Notas de crédito de un mes (el rango acota primero)"
GET /:id-empresa/invoices?date__gte=20260801&date__lte=20260831&type__in=3,8,13
```
```http title="Lo que emitió una API Key, acotado por fecha"
GET /:id-empresa/invoices?created__gte=2026-08-01&apiKeyId=3&ordering=-created
```
> **`date` y `created` no son lo mismo**
>
> `date` es la fecha **fiscal** del comprobante, la que declaraste al emitir. `created` es cuándo se emitió realmente. Para conciliar con tu sistema usá `created`; para reportes fiscales, `date`.
## Verlos en la app
Los mismos comprobantes están en la aplicación web, en:
**`https://asistentefacturaelectronica.com/app/comprobantes/`**
> **Los de homologación no se ven por defecto**
>
> Si emitiste con una clave `TEST_`, activá la **vista de homologación** en el listado. Sin eso sólo se muestran los comprobantes fiscales.
## Bitácora de la empresa
```http
GET /:id-empresa/audit-log
```
Registra las mutaciones **administrativas** —puntos de venta, API keys, certificados, datos fiscales— con qué se hizo, sobre qué, cuándo y quién. Es de sólo lectura.
Usa los mismos operadores, orden y paginado que el listado de comprobantes, pero con **su propio conjunto de campos**:
| Campo | Tipo | Corresponde a | Búsqueda |
| :--- | :--- | :--- | :--- |
| `action` | String | Qué se hizo: `pos.create`, `apikey.create`, `invoice.pdf.regenerate`… | **Directa** |
| `resourceId` | String | Sobre qué recurso | Directa **si va con `action`** |
| `actorType` | String | `user`, `apikey`, `admin` o `system` | Recorrido |
| `actorEmail` | String | Email del usuario que lo hizo | Recorrido |
| `actorApiKeyId` | Integer | API Key que lo hizo | Recorrido |
| `created` | String | Momento, ISO 8601 | Recorrido |
Igual que en los comprobantes, `resourceId` se indexa **junto a `action`**: `?action=invoice.pdf.regenerate&resourceId=1485` va directo, `?resourceId=1485` solo no.
> **La ventana de 90 días acota todo**
>
> La consulta devuelve siempre los **últimos 90 días** y ese corte se aplica primero, así que hasta un filtro de recorrido trabaja sobre una porción chica.
>
> Es una ventana fija: no se puede ampliar desde el query, y un `created__lte` más viejo no la corre hacia atrás.
La emisión de comprobantes **no** aparece acá: cada comprobante ya guarda su `apiKeyId` y su `userEmail`, y se consulta con `GET /invoices`.
---
# Consultar el padrón de ARCA y diagnosticar la conexión
> Consultá los datos de un contribuyente por CUIT o CUIL en el padrón de ARCA, y verificá el estado de la conexión, el certificado y los servidores antes de facturar.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/consultas-arca
## Consultar el padrón
```http
GET /:id-empresa/arca/padron/:docNumber
```
Devuelve los datos de un contribuyente a partir de su **CUIT o CUIL**, ya mapeados a los códigos que usa la emisión.
Por defecto se consulta la **Constancia de Inscripción**, que responde igual para personas físicas y jurídicas y trae todo lo que necesita `customer`: nombre o razón social, domicilio fiscal, provincia y condición frente al IVA. `result.source` dice siempre qué padrón contestó.
ARCA tiene un segundo padrón, el **Alcance 13**, que agrega actividades y teléfonos. Se pide con `?service=a13`, **y se delega aparte**:
| `?service=` | Padrón | Servicio a delegar en ARCA |
| :--- | :--- | :--- |
| *(default)* · `constancia` | Constancia de Inscripción | `ws_sr_constancia_inscripcion` |
| `a13` | Padrón Alcance 13 | `ws_sr_padron_a13` |
Los dos aceptan cualquier clave fiscal: la diferencia entre alcances es cuántos datos traen, no a quién pueden consultar. Con la constancia delegada alcanza para consultar a cualquier cliente.
```http
GET /:id-empresa/arca/padron/30703088534?service=a13
```
**No hay fallback al otro padrón.** Si el que corresponde no puede responder se devuelve el error, no los datos del otro: dos alcances pueden traer datos distintos de la misma clave, y un cambio silencioso de origen convierte "te falta delegar este servicio" en "tomá otro nombre".
> **El padrón se delega aparte del de facturación**
>
> Tener el certificado vinculado **no alcanza** para consultar el padrón: son servicios distintos y cada uno se autoriza por separado. Si no lo delegaste, este endpoint responde error aunque la emisión funcione perfecto.
>
> En el portal de ARCA, con tu Clave Fiscal, entrá a **Administrador de Relaciones de Clave Fiscal → Nueva Relación**, buscá los servicios de **padrón** (*Consulta a Padrón A13* y *Constancia de Inscripción*) y asociá el **alias del certificado** que creaste. Es el mismo procedimiento con el que autorizaste la facturación electrónica — ver [paso 3 de la guía del certificado](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca#paso-3--delegá-los-servicios).
>
> Para **homologación** el equivalente se hace desde el entorno de homologación de ARCA, donde el Administrador de Relaciones cumple la misma función.
```bash
curl https://nanofactura.com/api/mi-empresa/arca/padron/30703088534 \
-H "Authorization: Bearer PROD_tu_api_key"
```
```json
{
"ok": true,
"result": {
"name": "JUAN PEREZ",
"address": "CALLE FALSA 123 CABA CIUDAD AUTONOMA BUENOS AIRES",
"stateId": 0,
"taxCondition": 6,
"taxConditionName": "Responsable Monotributo",
"docType": 80,
"docNumber": 30703088534,
"source": "constancia",
"details": { }
}
}
```
| Campo | Se usa como |
| :--- | :--- |
| `name` | `customer.name` |
| `address` | `customer.address` |
| `stateId` | `customer.stateId` — puede venir `null` si no tiene domicilio cargado |
| `taxCondition` | `customer.taxType` — **tal cual, sin traducir** |
| `docType` | `customer.docType` |
| `docNumber` | `customer.docNumber` — el padrón lo devuelve **numérico** y la emisión lo espera **string**: convertilo |
| `source` | Qué padrón contestó: `constancia` o `a13` |
| `details` | La respuesta cruda de ARCA, por si necesitás algo que el mapeo no expone |
Tres respuestas que conviene distinguir:
| | |
| :--- | :--- |
| `404 NOT_FOUND` | ARCA no tiene esa clave. |
| `502 ARCA_CONNECTION_ERROR` | El padrón no pudo responder. Si el mensaje es `Computador no autorizado a acceder al servicio`, [falta delegarlo](https://asistentefacturaelectronica.com/ayuda/api/errores#502-arca_connection_error--computador-no-autorizado-a-acceder-al-servicio); si es `No se pudo conectar con…`, el servicio de ARCA está caído. |
| `400 VALIDATION_ERROR` | El `?service=` pedido no existe. |
> **Es la forma correcta de armar el `customer`**
>
> Si tenés el CUIT, consultá el padrón y usá lo que devuelve. Te asegura que la **condición frente al IVA** sea la real —que es lo que determina [qué comprobante corresponde emitir](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante)— y te trae la provincia ya codificada, sin que nadie la tipee mal.
>
> Guardá el resultado en tu base: no hace falta consultarlo en cada venta al mismo cliente.
## Condiciones de IVA admitidas
```http
GET /:id-empresa/arca/condicion-iva-receptor?claseCmp=A|B|C
```
Devuelve el catálogo de condiciones de IVA que ARCA acepta, opcionalmente filtrado por clase de comprobante. Sirve para poblar un desplegable en tu interfaz sin hardcodear la lista, y para verificar qué combinaciones son válidas.
La tabla completa, con los códigos: [Condiciones de IVA](https://asistentefacturaelectronica.com/ayuda/api/tablas/condiciones-iva).
## Diagnóstico de la conexión
```http
GET /:id-empresa/arca/status
```
Prueba en paralelo, contra el entorno de tu clave, la disponibilidad de los servidores de ARCA, la autenticación con tu certificado y el acceso a los padrones:
```json
{
"ok": true,
"environment": "PRODUCTION",
"certificateAlias": "nanofactura-prod",
"dummy": { "AppServer": "OK", "DbServer": "OK", "AuthServer": "OK", "duration": 412 },
"wsfe": { "ok": true, "error": null, "helpText": null, "reusedTicket": true, "duration": 890 },
"padronA13": { "ok": true, "error": null, "helpText": null, "reusedTicket": false, "duration": 763 },
"padronConstanciaInscripcion": { "ok": true, "error": null, "helpText": null, "reusedTicket": false, "duration": 812 }
}
```
Cuando algo falla, `error` trae el mensaje de ARCA y **`helpText` una explicación en castellano de qué suele causarlo**. Es la primera llamada a hacer cuando la emisión empieza a fallar y no sabés de qué lado está el problema.
| Bloque | Qué prueba |
| :--- | :--- |
| `dummy` | Los servidores de ARCA están arriba |
| `wsfe` | Tu certificado autentica contra facturación electrónica |
| `padronA13` | Acceso al padrón de personas humanas |
| `padronConstanciaInscripcion` | Acceso al padrón de personas jurídicas |
`reusedTicket` indica que la prueba usó el Ticket de Acceso que ya estaba vigente en lugar de pedir uno nuevo. ARCA entrega un solo ticket por certificado, servicio y entorno, dura 12 horas y no se puede revocar: pedir otro mientras vive devuelve `El CEE ya posee un TA valido`. Que haya un ticket vigente ya demuestra que ese certificado autentica contra ese servicio.
Si `wsfe` falla pero `dummy` está OK, el problema es tuyo: casi siempre falta [autorizar el servicio de facturación al certificado](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca#paso-3--delegá-los-servicios).
## Estado de los servidores
```http
GET /:id-empresa/arca/dummy
```
La versión liviana: sólo el estado de los servidores de ARCA, sin usar el certificado.
```json
{ "AppServer": "OK", "DbServer": "OK", "AuthServer": "OK" }
```
Útil para distinguir "ARCA se cayó" de "mi configuración está mal" cuando aparecen errores `502 ARCA_CONNECTION_ERROR`. Si acá algo no dice `OK`, el problema no es tuyo: esperá y reintentá.
> **Estos endpoints tienen más tiempo**
>
> Las rutas `/arca/*` tienen un techo de **25 segundos** en vez de los 15 habituales, porque dependen de la respuesta de los servidores de ARCA. Ver [Límites](https://asistentefacturaelectronica.com/ayuda/api/limites).
---
# Ejemplos de código en JavaScript, PHP y Python
> Repositorio público con implementaciones completas de facturación electrónica ARCA en JavaScript, PHP y Python, con constantes tipadas, manejo de errores y reintentos.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/ejemplos
Todo el código de esta página vive en un repositorio público, listo para clonar:
### 📦 [github.com/nanofactura/api-facturacion-electronica-ejemplos](https://github.com/nanofactura/api-facturacion-electronica-ejemplos)
Trae, para **JavaScript**, **PHP** y **Python**:
- Un **cliente** que resuelve autenticación, el envelope `{ ok, result }`, errores tipados, idempotencia y reintentos.
- Las **constantes** de todas las tablas de ARCA, para escribir `CONDICION_IVA.MONOTRIBUTO` en vez de `6`.
- **Diez ejemplos** numerados, los mismos en los tres lenguajes.
```bash
git clone https://github.com/nanofactura/api-facturacion-electronica-ejemplos.git
cd api-facturacion-electronica-ejemplos
cp .env.example .env # cargá tu API key
```
## Constantes en vez de números
El error más común al integrar es un número mal puesto: mandar `docType: 80` (CUIT) cuando el cliente te dio un DNI, o `taxType: 5` (21%) donde iba `taxType: 5` (consumidor final) — son campos distintos con códigos que se solapan.
Las constantes lo hacen imposible de confundir:
```js title="JavaScript"
import { TIPO_COMPROBANTE, CONDICION_IVA, TIPO_DOCUMENTO, PROVINCIAS } from './src/constants/index.js'
const factura = {
voucherType: TIPO_COMPROBANTE.FACTURA_C,
customer: {
docType: TIPO_DOCUMENTO.DNI,
docNumber: '35888999',
name: 'Pedro López',
taxType: CONDICION_IVA.CONSUMIDOR_FINAL,
stateId: PROVINCIAS.SANTA_FE,
},
items: [{ description: 'Consultoría', quantity: 1, price: 50000 }],
}
```
```php title="PHP"
use NanoFactura\Constants\{TipoComprobante, CondicionIva, TipoDocumento, Provincia};
$factura = [
'voucherType' => TipoComprobante::FACTURA_C,
'customer' => [
'docType' => TipoDocumento::DNI,
'docNumber' => '35888999',
'name' => 'Pedro López',
'taxType' => CondicionIva::CONSUMIDOR_FINAL,
'stateId' => Provincia::SANTA_FE,
],
'items' => [['description' => 'Consultoría', 'quantity' => 1, 'price' => 50000]],
];
```
```python title="Python"
from nanofactura.constants import TipoComprobante, CondicionIva, TipoDocumento, Provincia
factura = {
"voucherType": TipoComprobante.FACTURA_C,
"customer": {
"docType": TipoDocumento.DNI,
"docNumber": "35888999",
"name": "Pedro López",
"taxType": CondicionIva.CONSUMIDOR_FINAL,
"stateId": Provincia.SANTA_FE,
},
"items": [{"description": "Consultoría", "quantity": 1, "price": 50000}],
}
```
## Los once ejemplos
Numerados en orden de dificultad, iguales en los tres lenguajes:
| | Ejemplo | Qué muestra |
| :--- | :--- | :--- |
| `00` | La API cruda, sin cliente | El POST pelado, con todos los campos comentados: no hace falta ningún cliente |
| `01` | Verificar conexión | La primera llamada al integrar: valida clave, certificado y puntos de venta |
| `02` | Factura C — monotributo | El caso más común. Sin discriminar IVA |
| `03` | Factura A — responsable inscripto | Receptor con CUIT, IVA discriminado |
| `04` | Factura B — consumidor final | Cuándo hay que identificar al comprador |
| `05` | Servicios con período | `concept: 2` y las tres fechas obligatorias |
| `06` | Nota de crédito | Anular un comprobante con `associatedVouchers` |
| `07` | Consultar padrón | Armar el `customer` con los datos reales de ARCA |
| `08` | Descargar el PDF | A demanda, con espera si todavía se está generando |
| `09` | Listar comprobantes | Filtros, orden y paginado |
| `10` | Emisión en lote | Secuencial por punto de venta, con reintentos e idempotencia persistida |
Cada carpeta de lenguaje tiene su propio README con las instrucciones de instalación y ejecución.
## Configuración
Los tres lenguajes leen las mismas variables de entorno:
```bash title=".env"
NANOFACTURA_API_KEY=TEST_00000000000000000000000000000000
NANOFACTURA_ID_EMPRESA=mi-empresa
NANOFACTURA_BASE_URL=https://nanofactura.com/api
```
Empezá con una clave `TEST_`: los comprobantes de homologación no tienen validez fiscal. Cuando funcione, cambiás el valor por una `PROD_` y no tocás nada más.
¿No tenés todavía la clave, el certificado o los puntos de venta? Ver **[Puesta en marcha](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca)**.
## Para tu agente de IA
El repositorio incluye un `AGENTS.md` que apunta a la documentación en texto plano. Si trabajás con Claude Code, Cursor o similar, clonarlo y abrirlo alcanza para que el agente sepa cómo usar la API.
O directamente, sin clonar nada:
```text
Implementá facturación electrónica de ARCA leyendo:
https://asistentefacturaelectronica.com/ayuda/api/llms-full.txt
Seguí los patrones de https://github.com/nanofactura/api-facturacion-electronica-ejemplos
```
## Otros lenguajes y plataformas
La API es REST sobre HTTP con JSON: se consume desde **cualquier** lenguaje. Los tres del repositorio son ejemplos, no un requisito.
También funciona desde herramientas **no-code**: [n8n](https://n8n.io), [Make](https://make.com), Zapier o Google Apps Script pueden hacer el `POST` sin que escribas código. Ver las [preguntas frecuentes](https://asistentefacturaelectronica.com/ayuda/api/preguntas-frecuentes#con-qué-lenguajes-de-programación-me-puedo-integrar).
¿Te falta un ejemplo, o querés aportar el tuyo? Abrí un issue en el repositorio o escribinos a [contacto@nanofactura.com](mailto:contacto@nanofactura.com).
---
# Emitir un comprobante electrónico por API
> Referencia completa del endpoint de emisión de facturas electrónicas de ARCA. Campos, ítems, tributos, notas de crédito, cálculo de totales, idempotencia y respuesta con el CAE.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante
```http
POST /:id-empresa/invoices
```
La emisión es **síncrona**: la API le pide el CAE a ARCA y responde recién con la autorización en mano. Una emisión sana tarda entre 1 y 3 segundos.
Los **importes no se envían**: mandás cantidad, precio y alícuota por ítem, y la API calcula netos, IVA y totales con el redondeo que exige ARCA. Ver [cálculo de totales](#cálculo-de-totales).
## Campos principales
| Campo | Obligatorio | Tipo | En ARCA | Descripción |
| :--- | :---: | :--- | :--- | :--- |
| `idempotency` | **Sí** | String | — | Clave única de este comprobante. Máx. 128 caracteres: letras, números, `-` y `_`. Ver [idempotencia](#idempotencia). |
| `posNumber` | **Sí** | Integer | `PtoVta` | Punto de venta. Tiene que estar dado de alta en NanoFactura **y** habilitado en ARCA. |
| `voucherType` | **Sí** | Integer | `CbteTipo` | Tipo de comprobante ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/tipos-de-comprobante)). |
| `concept` | **Sí** | Integer | `Concepto` | `1` productos · `2` servicios · `3` productos y servicios. |
| `voucherDate` | **Sí** | String | `CbteFch` | Fecha del comprobante, `YYYYMMDD`. |
| `customer` | **Sí** | Object | — | Datos del receptor (ver [tabla](#cliente-customer)). |
| `items` | **Sí** | Array | — | Líneas de detalle, mínimo 1 (ver [tabla](#ítems-items)). |
| `serviceFrom` | Condicional | String | `FchServDesde` | Inicio del servicio, `YYYYMMDD`. **Obligatorio si `concept` es `2` o `3`.** |
| `serviceTo` | Condicional | String | `FchServHasta` | Fin del servicio, `YYYYMMDD`. **Obligatorio si `concept` es `2` o `3`.** |
| `serviceExpiration` | Condicional | String | `FchVtoPago` | Vencimiento del pago, `YYYYMMDD`. **Obligatorio si `concept` es `2` o `3`.** |
| `associatedVouchers` | Condicional | Array | `CbtesAsoc` | Comprobante que se modifica. **Necesario en notas de crédito y débito** (ver [tabla](#comprobantes-asociados)). |
| `currency` | No | Object | — | Moneda. Por defecto, pesos argentinos (ver [tabla](#moneda-currency)). |
| `tributes` | No | Array | `Tributos` | Impuestos y percepciones que no son IVA (ver [tabla](#tributos-tributes)). |
| `emails` | No | Array | — | Destinatarios del envío automático del PDF (ver [tabla](#correos-emails)). |
| `settings` | No | Object | — | Precisión decimal de cantidades y precios (ver [tabla](#precisión-settings)). |
| `legend` | No | String | — | Leyenda impresa en el comprobante. Máx. 200 caracteres. |
| `comments` | No | String | — | Comentario interno del emisor, no se imprime. Máx. 512 caracteres. |
| `externalReference` | No | String | — | Identificador de tu sistema. Máx. 128 caracteres. **Filtrable en el listado.** |
| `additionalData` | No | Object | — | Objeto clave-valor libre que se guarda junto al comprobante. |
| `cancelsInSameForeignCurrency` | No | String | `CanMisMonExt` | `'S'` o `'N'`. Sólo aplica en moneda extranjera. |
| `receiverTaxConditionId` | No | Integer | `CondicionIVAReceptorId` | Si se envía, **pisa** a `customer.taxType`. Normalmente no hace falta. |
> **Guardá `externalReference`**
>
> Es el campo que ata el comprobante a tu operación (número de pedido, id de venta). Es el único que después podés usar para buscarlo desde tu sistema sin llevar una tabla de equivalencias propia.
Qué campos son obligatorios **según tu condición fiscal y el tipo de comprobante**: ver **[Equivalencia de campos con ARCA](https://asistentefacturaelectronica.com/ayuda/api/campos-arca)**.
## Cliente (`customer`)
| Campo | Obligatorio | Tipo | En ARCA | Descripción |
| :--- | :---: | :--- | :--- | :--- |
| `docType` | **Sí** | Integer | `DocTipo` | Tipo de documento ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/tipos-de-documento)). |
| `docNumber` | **Sí** | String | `DocNro` | Número sin guiones ni puntos. |
| `name` | **Sí** | String | — | Nombre, apellido o razón social. |
| `taxType` | **Sí** | Integer | `CondicionIVAReceptorId` | Condición frente al IVA ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/condiciones-iva)). |
| `address` | No | String | — | Domicilio. Se imprime en el comprobante. |
| `stateId` | Recomendado | Integer \| null | — | Provincia ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/provincias)). |
> **Mandá siempre `stateId`**
>
> Es opcional a nivel técnico, pero es **el único dato estructurado de ubicación** del receptor: `address` es texto libre y no se puede agrupar. Con la provincia cargada podés después responder por jurisdicción —dónde está tu facturación, qué te corresponde para **Ingresos Brutos**, cómo se reparte tu cartera— y armar reportes de impuestos sin trabajo manual. Sin ella, esa pregunta no tiene respuesta y **no hay forma de reconstruirla hacia atrás**.
>
> Si genuinamente no sabés de dónde es el receptor, mandá `null`: significa "no informada" y es mejor que inventar `0`, que es CABA.
Podés reenviar tal cual el `stateId` que devuelve la [consulta de padrón](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca).
## Ítems (`items`)
Mínimo uno. Cada objeto es una línea de detalle.
| Campo | Obligatorio | Tipo | Descripción |
| :--- | :---: | :--- | :--- |
| `description` | **Sí** | String | Descripción del producto o servicio. |
| `quantity` | **Sí** | Number | Cantidad. Mínimo `0.0001`. |
| `price` | **Sí** | Number | Precio unitario, sin descuento. Mínimo `0`. |
| `taxType` | **Sí**, salvo en C | Integer | Alícuota de IVA de la línea ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/alicuotas-iva)). Los comprobantes C no la llevan: no discriminan IVA. |
| `code` | No | String | Tu código interno del producto. |
| `unitType` | No | Integer | Unidad de medida ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/unidades-de-medida)). Por defecto `7` (unidades). |
| `discount` | No | Number | Descuento de la línea. Por defecto `0`. |
| `discountType` | No | Enum | `'percentage'` (defecto) o `'amount'`. |
## Moneda (`currency`)
| Campo | Obligatorio | Tipo | En ARCA | Descripción |
| :--- | :---: | :--- | :--- | :--- |
| `id` | No | String | `MonId` | Código de moneda ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/monedas)). Por defecto `'PES'`. |
| `quotation` | No | Number | `MonCotiz` | Cotización contra el peso. Por defecto `1`. |
Si usás una moneda distinta de `PES`, **mandá la cotización del día**: no se consulta sola.
## Tributos (`tributes`)
Impuestos y percepciones que **no son IVA**: Ingresos Brutos, tasas municipales, impuestos internos, percepciones.
| Campo | Obligatorio | Tipo | En ARCA | Descripción |
| :--- | :---: | :--- | :--- | :--- |
| `type` | **Sí** | Integer | `Id` | Tipo de tributo ([tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/tributos)). |
| `baseAmount` | **Sí** | Number | `BaseImp` | Base imponible. |
| `aliquot` | **Sí** | Number | `Alic` | Alícuota aplicada. |
| `subtotal` | **Sí** | Number | `Importe` | Importe del tributo. |
| `description` | No | String | `Desc` | Descripción. |
A diferencia de los ítems, acá **el importe sí lo calculás vos**: ARCA recibe el `subtotal` tal como lo mandás.
## Comprobantes asociados
Vincula la factura original que una **nota de crédito** o **nota de débito** modifica.
| Campo | Obligatorio | Tipo | En ARCA | Descripción |
| :--- | :---: | :--- | :--- | :--- |
| `voucherType` | **Sí** | Integer | `Tipo` | Tipo del comprobante asociado. |
| `posNumber` | **Sí** | Integer | `PtoVta` | Punto de venta del asociado. |
| `voucherNumber` | **Sí** | Integer | `Nro` | Número del asociado. |
| `cuit` | No | String | `Cuit` | CUIT del emisor, sólo si es distinto al tuyo. |
```json title="Nota de crédito C que anula la factura C 0001-00000045"
{
"voucherType": 13,
"associatedVouchers": [{ "voucherType": 11, "posNumber": 1, "voucherNumber": 45 }]
}
```
La nota de crédito debe ser **de la misma letra** que la factura que anula. Ver [tipos de comprobante](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante).
## Correos (`emails`)
Despacha el PDF por correo en segundo plano, sin bloquear la respuesta.
| Campo | Obligatorio | Tipo | Descripción |
| :--- | :---: | :--- | :--- |
| `email` | **Sí** | String | Dirección del destinatario. |
| `name` | No | String | Nombre del destinatario. |
| `comments` | No | String | Texto a incluir en el cuerpo del correo. |
| `pdfVersions` | No | Array | Copias a adjuntar: `1` original, `2` duplicado, `3` triplicado. Por defecto `[1]`. |
Máximo **3 destinatarios por comprobante**, contando los que agregues después.
## Precisión (`settings`)
| Campo | Tipo | Descripción |
| :--- | :--- | :--- |
| `quantityPrecision` | Integer | Decimales de la cantidad: `2` (defecto), `4` o `6`. |
| `pricePrecision` | Integer | Decimales del precio unitario: `2` (defecto), `4` o `6`. |
Subilos si vendés por unidades fraccionadas o con precios de más de dos decimales. Los importes resultantes se redondean siempre a 2 decimales, que es lo que exige ARCA.
## Cálculo de totales
Por cada ítem, en este orden y redondeando a 2 decimales en cada paso:
```text
subtotal = quantity × price
descuento = discountType === 'percentage' ? subtotal × discount / 100 : discount
neto = subtotal − descuento
```
El destino del neto depende del `taxType` de la línea:
| `taxType` | Tratamiento | Va a | ¿Genera IVA? |
| :--- | :--- | :--- | :---: |
| `4`, `5`, `6`, `8`, `9` | Gravado | `ImpNeto` | Sí |
| `2`, `3` | Exento / 0% | `ImpOpEx` | No |
| `1` u otro | No gravado | `ImpTotConc` | No |
Y el total es `ImpNeto + ImpIVA + ImpTrib + ImpOpEx + ImpTotConc`.
> **Los comprobantes C no discriminan IVA**
>
> En **Factura C, Nota de Débito C y Nota de Crédito C** (`11`, `12`, `13`) **todas** las líneas van a `ImpNeto`, `ImpIVA` queda en `0` y no se informa IVA. Por eso los ítems de un comprobante C **no llevan `taxType`**: no hay alícuota que declarar. Si llega igual, se acepta y se ignora.
>
> Es correcto y es lo que corresponde: un monotributista no discrimina IVA.
### Lo que la API completa sola
| Campo ARCA | De dónde sale |
| :--- | :--- |
| `CbteDesde` / `CbteHasta` | Número correlativo que reserva la API. |
| `ImpNeto` | Suma de las líneas gravadas (y de **todas** en comprobantes C). |
| `ImpIVA` | Suma del IVA calculado por línea. |
| `ImpOpEx` | Suma de las líneas exentas o al 0%. |
| `ImpTotConc` | Suma de las líneas no gravadas. |
| `ImpTrib` | Suma de los `tributes[].subtotal`. |
| `ImpTotal` | La suma de todo lo anterior. |
| `Iva[]` | Una entrada por cada alícuota gravada presente en los ítems. |
| `Tributos[]` | Una entrada por cada elemento de `tributes`. |
## Idempotencia
`idempotency` identifica **un** comprobante. Es lo que hace segura la red: si se corta la conexión y no sabés si el comprobante se emitió, reintentás con la **misma** clave y la API te devuelve el original en vez de emitir uno nuevo.
```json
{ "ok": true, "result": { "id": 1485, "cae": "76251489623547", "idempotencyHit": true } }
```
`idempotencyHit: true` significa "esto ya existía, no se emitió nada nuevo".
### Cómo armar la clave
**Una clave identifica exactamente un comprobante.** No se reparte por tipo ni por punto de venta: si mandás una clave que ya pertenece a un comprobante de **otro** punto de venta o de otro tipo, el pedido se rechaza con `409`, y es un error permanente.
```json
{
"ok": false,
"error": "La clave de idempotencia ya corresponde al comprobante 1-6-45, no al que se está pidiendo (2-6). Usá una clave distinta para cada comprobante.",
"code": "VALIDATION_ERROR"
}
```
> **Derivala de tu dominio, incluyendo lo que distingue al comprobante**
>
> Lo más robusto **no** es un UUID al azar: es una clave **determinística**, que podés recalcular sin haberla guardado. Si tu proceso se cae después de emitir pero antes de registrar el resultado, con un UUID perdiste la clave y el reintento duplica el comprobante; con una clave derivada la volvés a calcular igual y recuperás el original.
>
> La condición es que incluya **todo lo que hace único al comprobante**, no sólo la operación:
>
> ```text
> {tuIdDeVenta}-{voucherType}-{posNumber}
>
> venta-8842-11-1 Factura C de la venta 8842
> venta-8842-13-1 su nota de crédito
> ```
>
> Así el mismo pedido puede tener su factura y su nota de crédito sin chocar. Sólo `venta-8842` para las dos daría `409` en la segunda.
>
> Si preferís un UUID, es igual de válido —pero **guardalo junto a la venta antes de emitir**, no después.
Sólo se admiten letras, números, `-` y `_`, hasta 128 caracteres. Las claves son **independientes por entorno**: la misma puede existir en homologación y en producción sin pisarse, así que podés ensayar con las claves reales.
## Respuesta (`201 Created`)
```json
{
"ok": true,
"result": {
"id": 1485,
"voucherNumber": 45,
"cae": "76251489623547",
"totalAmount": 142500,
"arcaObservations": null,
"idempotencyHit": false
},
"quota": {
"type": "invoices",
"granted": 500,
"used": 138,
"remaining": 362,
"overage": 0,
"periodEnd": "2026-09-16",
"breakdown": { "production": 112, "homologation": 26 }
}
}
```
| Campo | Descripción |
| :--- | :--- |
| `id` | Identificador interno. Es el que se usa en las rutas de PDF y email. |
| `voucherNumber` | Número correlativo dentro del punto de venta y tipo. |
| `cae` | CAE otorgado por ARCA. |
| `totalAmount` | Importe total calculado. |
| `arcaObservations` | `null`, o un array `[{ code, msg }]` con observaciones que ARCA devolvió **aprobando igual** el comprobante. Conviene registrarlas. |
| `idempotencyHit` | `true` si la clave ya correspondía a un comprobante existente. |
> ****Guardá `id`, `cae`, `voucherNumber`, `posNumber` y `voucherType`.** Con eso alcanza para todo lo demás.**
>
>
El PDF **no** está listo en este momento: se genera en segundo plano. Ver [PDF y envío por email](https://asistentefacturaelectronica.com/ayuda/api/pdf-y-email).
### El objeto `quota`
Va **al lado** de `result`, no adentro: es el estado del cupo mensual de tu plan, no un dato del comprobante. Sirve para frenar antes de chocar sin consultar nada aparte.
| Campo | Descripción |
| :--- | :--- |
| `granted` | Cupo total del período (los adicionales contratados ya están sumados). |
| `used` | Consumido, **incluyendo este comprobante**. |
| `remaining` | `granted − used`, con piso en 0. Nunca es negativo. |
| `periodEnd` | Día en que se renueva el cupo. |
| `breakdown` | Consumo por entorno. |
Tres cosas a tener en cuenta: el cupo es **mensual y no se acumula**; **homologación consume** el mismo cupo; y `quota` **puede venir en `null`** (en reintentos idempotentes, que no consumen) — tu cliente tiene que tolerarlo. `null` no significa "cero disponible", significa "no hay nada que informar".
> **`quota` no es parte del contrato estable**
>
> A diferencia de `result`, la forma de este objeto **puede cambiar**: pueden aparecer campos nuevos, y los actuales pueden dejar de estar. Usalo para mostrar o alertar, no como algo de lo que dependa tu emisión.
>
> En concreto: leelo siempre de forma defensiva (`quota?.remaining`), tolerá que sea `null` y no lo uses como condición para decidir si emitís. La única fuente confiable de que el cupo se agotó es el error [`429 SUBSCRIPTION_QUOTA_EXCEEDED`](https://asistentefacturaelectronica.com/ayuda/api/errores).
## Errores
Los cuatro que vas a ver en la práctica:
| HTTP | `code` | Qué pasó | ¿Reintentar? |
| :---: | :--- | :--- | :--- |
| `400` | `VALIDATION_ERROR` | Payload inválido o punto de venta inexistente | No, corregí el pedido |
| `422` | `ARCA_ERROR` | ARCA rechazó el comprobante | No, corregí el pedido |
| `429` | `VOUCHER_LOCK_TIMEOUT` | Otra emisión del mismo punto de venta sigue en curso | **Sí**, con la misma `idempotency` |
| `504` | — | Se agotó el tiempo del pedido | **Sí**, con la misma `idempotency` |
Un `504` **no** significa que el comprobante no se haya emitido. Reintentá con la misma `idempotency`: si se había emitido, lo recuperás.
La lista completa, con qué hacer en cada caso: **[Errores](https://asistentefacturaelectronica.com/ayuda/api/errores)**.
## Emitir en volumen
ARCA exige numeración consecutiva por punto de venta y tipo, así que la API emite **de a un comprobante por vez para cada combinación `posNumber` + `voucherType`**. Si llega un pedido mientras otro se está autorizando, espera su turno solo (hasta 20 segundos).
**Secuenciá los pedidos de un mismo punto de venta y tipo.** Lanzarlos en paralelo no acelera nada —igual se serializan— y sólo produce errores `429`. Distintos puntos de venta sí pueden emitir en paralelo. No intentes paralelizar llamados a la API, solo generarás errores.
Ver [Límites y buenas prácticas](https://asistentefacturaelectronica.com/ayuda/api/limites).
## Ejemplo completo
```json title="Factura C de servicios, con período, descuento y envío por email"
{
"idempotency": "9f1c3a5e-7b2d-4c81-9e6f-0a2b4c6d8e10",
"posNumber": 1,
"voucherType": 11,
"concept": 2,
"voucherDate": "20260613",
"serviceFrom": "20260601",
"serviceTo": "20260613",
"serviceExpiration": "20260630",
"customer": {
"docType": 96,
"docNumber": "35888999",
"name": "Pedro López",
"address": "Av. de Mayo 500, CABA",
"stateId": 0,
"taxType": 5
},
"items": [
{
"code": "SRV-DEV",
"description": "Servicios de desarrollo de software — Junio 2026",
"quantity": 1,
"unitType": 7,
"price": 150000,
"discount": 5,
"discountType": "percentage"
}
],
"emails": [
{ "email": "pedro.lopez@example.com", "name": "Pedro López", "pdfVersions": [1] }
],
"externalReference": "EXT-ORD-99882",
"legend": "Gracias por su compra"
}
```
Implementaciones completas en JavaScript, PHP y Python: **[Ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos)**.
---
# Errores de la API y cómo manejarlos
> Todos los códigos de error de la API de facturación electrónica, cuándo aparecen y cuáles conviene reintentar. Incluye rechazos de ARCA, timeouts e idempotencia.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/errores
Todas las respuestas siguen la misma forma:
```json title="Éxito"
{ "ok": true, "result": { } }
```
```json title="Error"
{ "ok": false, "error": "Mensaje explicativo", "code": "VALIDATION_ERROR" }
```
**Chequeá `ok`, no el status HTTP.** `code` acompaña a los errores de negocio; los de infraestructura (límite de peticiones, timeout) traen sólo `error`. Algunos errores de validación agregan `details` con el detalle campo por campo.
## Tabla completa
| HTTP | `code` | Cuándo aparece | ¿Reintentar? |
| :---: | :--- | :--- | :--- |
| `400` | `VALIDATION_ERROR` | Payload inválido, punto de venta inexistente, faltan fechas de servicio | **No** — corregí el pedido |
| `401` | — | Falta el `Authorization`, clave inválida o revocada, ruta no habilitada | **No** |
| `402` | `SUBSCRIPTION_EXPIRED` | El período contratado de la empresa venció | Recién al regularizar el pago |
| `404` | `NOT_FOUND` | El comprobante o el punto de venta no existe, o el documento no está en el padrón de ARCA | **No** |
| `409` | `VALIDATION_ERROR` | Clave de idempotencia reusada para otro comprobante | **No** — generá una clave nueva |
| `409` | `PDF_NOT_READY` | El PDF todavía se está generando | **Sí**, en unos segundos |
| `422` | `ARCA_ERROR` | ARCA rechazó el comprobante | **No** — corregí el pedido |
| `429` | — | Límite de peticiones | **Sí**, con espera |
| `429` | `VOUCHER_LOCK_TIMEOUT` | Otra emisión del mismo punto de venta sigue en curso | **Sí**, ver `Retry-After` |
| `429` | `QUOTA_EXCEEDED` | Se agotó la cuota de una operación administrativa | **Sí**, pasada la ventana |
| `429` | `SUBSCRIPTION_QUOTA_EXCEEDED` | Se agotó la cuota mensual de comprobantes | Recién al ampliar el límite o al renovarse el período |
| `500` | — | Error interno | **Sí**, con backoff |
| `502` | `ARCA_CONNECTION_ERROR` | No se pudo hablar con los servidores de ARCA | **Sí**, con backoff — salvo que sea el [certificado sin delegar](#502-arca_connection_error--computador-no-autorizado-a-acceder-al-servicio) |
| `504` | — | Se agotó el tiempo del pedido | **Sí**, con la misma `idempotency` |
## Los que importan
### `422 ARCA_ERROR` — ARCA rechazó el comprobante
Es un rechazo de **negocio**: el comprobante no se guardó y **no consumió numeración**.
```json
{
"ok": false,
"error": "(10015) Factura B con importe total mayor a $ … requiere identificación del comprador",
"code": "ARCA_ERROR",
"details": [{ "code": 10015, "msg": "Factura B con importe total mayor a …" }]
}
```
`details` trae el código y el mensaje exactos de ARCA. **Registralos**: son la única forma de diagnosticar qué regla se violó.
Reintentar sin cambiar nada va a fallar igual. Los motivos más comunes:
| Código | Qué significa | Cómo se corrige |
| :--- | :--- | :--- |
| `10015` | Factura B/C sobre el umbral sin identificar al comprador | Pedile DNI o CUIT al cliente |
| — | Documento inválido para el tipo de comprobante | Factura A exige CUIT; ver [qué comprobante emitir](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante) |
| — | Fecha fuera del rango permitido | ARCA acota cuánto podés retroceder o adelantar la fecha |
| — | Punto de venta no habilitado | Tiene que ser de tipo **webservice**; ver [puntos de venta](https://asistentefacturaelectronica.com/ayuda/api/puntos-de-venta) |
### `504` — se agotó el tiempo
> **Un `504` no significa que el comprobante no se emitió**
>
> Puede haberse emitido y el corte haber ocurrido mientras volvía la respuesta. **Reintentá con la misma `idempotency`**: si ya existía, lo recuperás con `idempotencyHit: true`; si no, se emite ahora. Nunca se duplica.
>
> Es exactamente el problema para el que existe la clave de idempotencia. Generar una clave nueva en el reintento sí puede duplicar el comprobante.
### `429 VOUCHER_LOCK_TIMEOUT` — otra emisión en curso
ARCA exige numeración consecutiva, así que se emite de a un comprobante por vez para cada combinación de punto de venta y tipo. Si llega un pedido mientras otro se autoriza, **espera su turno solo**. Este error aparece únicamente si pasados esos segundos la emisión anterior sigue en curso.
Es **transitorio y seguro de reintentar**: el comprobante no se emitió y el número quedó libre. Viene con el header `Retry-After`. Reintentá con **la misma `idempotency`**.
Si lo ves seguido, estás emitiendo en paralelo sobre el mismo punto de venta: **secuenciá**. Ver [Límites](https://asistentefacturaelectronica.com/ayuda/api/limites#emitir-en-volumen).
### `409` — clave de idempotencia reusada
```json
{
"ok": false,
"error": "La clave de idempotencia ya corresponde al comprobante 1-6-45, no al que se está pidiendo (2-6). Usá una clave distinta para cada comprobante.",
"code": "VALIDATION_ERROR"
}
```
La clave ya pertenece a un comprobante de **otro** punto de venta o de otro tipo. Es **permanente**: reintentar falla igual. Generá una clave nueva, y usá un UUID por comprobante.
### `402 SUBSCRIPTION_EXPIRED` — período vencido
```json
{
"ok": false,
"error": "El período contratado de la empresa venció. Regularizá el pago para volver a emitir comprobantes.",
"code": "SUBSCRIPTION_EXPIRED"
}
```
No es un problema de credenciales —la clave sigue siendo válida—, por eso responde `402` y no `401`. No consume numeración. Se resuelve abonando la renovación en la app, en **Administrar → Pagos**.
### `429 SUBSCRIPTION_QUOTA_EXCEEDED` — cuota mensual agotada
```json
{
"ok": false,
"error": "Se agotó la cuota de 500 comprobantes de este período hasta el 2026-09-16. …",
"code": "SUBSCRIPTION_QUOTA_EXCEEDED",
"details": {
"quota": { "granted": 500, "used": 500, "remaining": 0, "periodEnd": "2026-09-16" }
}
}
```
La suscripción **está al día**: lo que se agotó es la capacidad del mes. Cede al ampliar el límite o al renovarse el período, así que **no corresponde un backoff automático** — reintentar antes de eso no cambia nada.
`details.quota` te deja distinguir los dos casos sin leer el mensaje:
Para no chocar de sorpresa: cada emisión devuelve `quota.remaining`, y al 90% del cupo la cuenta recibe un aviso por email.
### `502 ARCA_CONNECTION_ERROR` — "Computador no autorizado a acceder al servicio"
```json
{
"ok": false,
"error": "WSAA Fault: Computador no autorizado a acceder al servicio",
"code": "ARCA_CONNECTION_ERROR"
}
```
El certificado está cargado y activo, pero **su alias no está delegado al servicio** que se está usando. Cada servicio de ARCA se autoriza por separado:
| Qué estabas haciendo | Servicio que hay que delegar |
| :--- | :--- |
| Emitir un comprobante | **Facturación Electrónica** (`wsfe`) |
| [Consultar el padrón](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca) de una persona física | **Consulta a Padrón A13** |
| Consultar el padrón de una persona jurídica | **Constancia de Inscripción** |
Se resuelve en el portal de ARCA, en el **Administrador de Relaciones de Clave Fiscal** (en homologación, desde **WSASS**), asociando el alias del certificado al servicio que falta: **[Vincular tu certificado de ARCA → Paso 4](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca)**.
> **Es un `502`, pero no cede reintentando**
>
> Llega como error de conexión porque falla la autenticación contra ARCA, no la emisión. Reintentar agota los intentos y termina fallando igual: hay que hacer la delegación. Es la causa número uno de "tengo el certificado cargado y no puedo facturar".
`GET /:id-empresa/arca/status` te dice **cuál** de los servicios está fallando —conexión, autenticación y padrones se prueban por separado— y devuelve la sugerencia correspondiente. Ver [consultas a ARCA](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca).
## Cómo manejarlos
Una regla simple que cubre todos los casos:
```js
async function emitir(payload, intentos = 3) {
// La clave se genera UNA vez, fuera del bucle: es lo que hace que el
// reintento recupere el comprobante en vez de emitir otro.
const body = { ...payload, idempotency: payload.idempotency ?? crypto.randomUUID() }
for (let intento = 1; intento <= intentos; intento++) {
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body) })
const data = await res.json()
if (data.ok) return data.result
// Errores permanentes: reintentar no cambia nada.
if ([400, 401, 402, 403, 404, 409, 422].includes(res.status)) {
throw new Error(`${data.code}: ${data.error}`)
}
// La cuota mensual tampoco cede reintentando.
if (data.code === 'SUBSCRIPTION_QUOTA_EXCEEDED') {
throw new Error(data.error)
}
// El resto (429, 500, 502, 504) es transitorio.
if (intento === intentos) throw new Error(data.error ?? 'Error tras varios intentos')
const retryAfter = Number(res.headers.get('Retry-After')) || 2 ** intento
await new Promise((r) => setTimeout(r, retryAfter * 1000))
}
}
```
Las tres decisiones que importan:
1. **La `idempotency` se genera una sola vez**, fuera del bucle. Es lo único que hace seguro el reintento.
2. **Los errores permanentes no se reintentan.** Un `422` reintentado tres veces sigue siendo un `422`, y sólo suma latencia.
3. **Respetá `Retry-After`** cuando venga; si no, backoff exponencial.
Implementaciones completas con esta lógica en JavaScript, PHP y Python: **[Ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos)**.
---
# API de Facturación Electrónica ARCA
> API REST de NanoFactura para emitir facturas electrónicas de ARCA (ex AFIP) desde cualquier sistema. Integración en JavaScript, PHP, Python o cualquier lenguaje con HTTP. Obtené el CAE en una sola llamada.
Fuente: https://asistentefacturaelectronica.com/ayuda/api
**NanoFactura** ofrece una **API REST** para emitir comprobantes electrónicos de **ARCA (ex AFIP)** desde tu propio sistema. Mandás un JSON con el cliente y los ítems, y recibís el **CAE** en la misma respuesta.
Una sola llamada reemplaza todo el trabajo de integrarte por tu cuenta: firma de certificados, renovación de tokens, armado del XML, protocolo SOAP, numeración correlativa, cálculos de totales, IVA, generación del PDF y envío por email.
**curl**
```bash
curl -X POST https://nanofactura.com/api/mi-empresa/invoices \
-H "Authorization: Bearer PROD_tu_api_key" \
-H "Content-Type: application/json" \
-d '{
"idempotency": "venta-8842-11-1",
"posNumber": 1,
"voucherType": 11,
"concept": 1,
"voucherDate": "20260811",
"customer": { "docType": 99, "docNumber": "0", "name": "Consumidor Final", "taxType": 5 },
"items": [{ "description": "Servicio de consultoría", "quantity": 1, "price": 50000 }]
}'
```
**JavaScript**
```js
const res = await fetch('https://nanofactura.com/api/mi-empresa/invoices', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NANOFACTURA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
idempotency: 'venta-8842-11-1',
posNumber: 1,
voucherType: 11,
concept: 1,
voucherDate: '20260811',
customer: { docType: 99, docNumber: '0', name: 'Consumidor Final', taxType: 5 },
items: [{ description: 'Servicio de consultoría', quantity: 1, price: 50000 }],
}),
})
const data = await res.json()
console.log(data.result.cae)
```
**PHP**
```php
$ch = curl_init('https://nanofactura.com/api/mi-empresa/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NANOFACTURA_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'idempotency' => 'venta-8842-11-1',
'posNumber' => 1,
'voucherType' => 11,
'concept' => 1,
'voucherDate' => '20260811',
'customer' => ['docType' => 99, 'docNumber' => '0', 'name' => 'Consumidor Final', 'taxType' => 5],
'items' => [['description' => 'Servicio de consultoría', 'quantity' => 1, 'price' => 50000]],
], JSON_UNESCAPED_UNICODE),
]);
$data = json_decode(curl_exec($ch), true);
echo $data['result']['cae'];
```
**Python**
```python
import os, requests
response = requests.post(
"https://nanofactura.com/api/mi-empresa/invoices",
headers={"Authorization": f"Bearer {os.environ['NANOFACTURA_API_KEY']}"},
json={
"idempotency": "venta-8842-11-1",
"posNumber": 1,
"voucherType": 11,
"concept": 1,
"voucherDate": "20260811",
"customer": {"docType": 99, "docNumber": "0", "name": "Consumidor Final", "taxType": 5},
"items": [{"description": "Servicio de consultoría", "quantity": 1, "price": 50000}],
},
timeout=30,
)
print(response.json()["result"]["cae"])
```
Y la respuesta, en cualquiera de los cuatro:
```json
{ "ok": true, "result": { "id": 1485, "voucherNumber": 45, "cae": "76251489623547", "totalAmount": 50000 } }
```
## Implementala con tu agente de IA
Pegá esto en **Claude Code**, **Cursor**, **Antigravity**, **Copilot** o el agente que uses:
```text title="Prompt para tu agente de programación"
Implementá la emisión de facturas electrónicas de ARCA en este proyecto.
Leé primero la documentación completa de la API:
https://asistentefacturaelectronica.com/ayuda/api/llms-full.txt
Ejemplos de referencia en JavaScript, PHP y Python:
https://github.com/nanofactura/api-facturacion-electronica-ejemplos
Mi API key está en la variable de entorno NANOFACTURA_API_KEY.
Empezá verificando la conexión con GET /:id-empresa/api-keys/test.
```
> **Otros formatos**
>
> Cada página tiene también su versión en Markdown crudo agregando `.md` a la URL: por ejemplo [`/ayuda/api/emitir-comprobante.md`](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante.md). Y el índice del sitio completo está en [`/ayuda/llms.txt`](https://asistentefacturaelectronica.com/ayuda/llms.txt).
## Cómo se integra
Tres pasos, una sola vez:
1. **[Vinculá tu certificado con ARCA](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca)** — generás el pedido de certificado desde la app, lo tramitás en el portal de ARCA y lo subís. Es el paso que autoriza a NanoFactura a facturar en tu nombre.
2. **[Configurá tus puntos de venta](https://asistentefacturaelectronica.com/ayuda/api/puntos-de-venta)** — los das de alta en ARCA y en NanoFactura.
3. **[Creá tu API Key](https://asistentefacturaelectronica.com/ayuda/api/api-keys)** — el prefijo de la clave decide si facturás contra **homologación** (pruebas) o **producción** (fiscal).
Después, para [emitir un comprobante](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante) es un `POST`.
👉 **¿Vas con apuro?** Andá directo a **[Primeros pasos](https://asistentefacturaelectronica.com/ayuda/api/primeros-pasos)**: primera factura de prueba en cinco minutos.
## Qué resuelve por vos
| | |
| :--- | :--- |
| **PDF con logo y email** | La plantilla PDF de los comprobantes es configurable en colores, tipografías, logo, campos personalizados. Se puede enviar al cliente por mail sin que tengas que conectar otros servicios. |
| **1 CUIT, Varios negocios** | Podés configurar cada punto de venta con su propio nombre de fantasía y logo. Útil si tenés varias unidades comerciales bajo mismo CUIT. |
| **Múltiples CUIT** | Generá una empresa Premium por cada CUIT y facturá con la API sin problemas para varios CUIT. |
| **Cambios de normativa** | Cuando ARCA cambia algo, lo actualizamos nosotros. Tu código no se toca. |
| **Cálculo de importes** | ARCA Es quisquillosa. Mandás cantidad, precio y alícuota; los netos, el IVA y los totales los calcula la API con el redondeo que exige ARCA. |
| **Emisión síncrona** | Pedís el comprobante y obtenés la autorización de ARCA (CAE) en la misma respuesta, en 1 a 3 segundos. |
| **Almacenamiento de comprobantes** | Guardamos tus PDF y toda la infomración de de manera segura por 5 años. Accedés cuando quieras. Tu información siempre es tuya. |
| **Todo REST** | Nada de pelear con SOAP ni XML. Conectate con el lenguaje de programación de tu proyecto |
## Sobre NanoFactura y Asistente Factura Electrónica
Son dos productos de la misma plataforma, y conviene no confundirlos:
- **NanoFactura** es el proveedor de la **API**. Es lo que documenta esta sección y lo que usás para facturar desde tu sistema.
- **[Asistente Factura Electrónica](https://asistentefacturaelectronica.com/ayuda/)** es la **extensión de Chrome** de NanoFactura, para quienes facturan a mano en *Comprobantes en Línea* de ARCA.
Con tu misma cuenta/suscripción ambos acceden a tu misma empresa y mismo listado de comprobantes. Podés usar los dos a la vez: lo que emitís por API lo ves en la app web, y al revés.
## Información general
| | |
| :--- | :--- |
| **URL base** | `https://nanofactura.com/api` |
| **Formato** | JSON en pedido y respuesta |
| **Autenticación** | `Authorization: Bearer ` |
| **Entornos** | Homologación (`TEST_`) y producción (`PROD_`), según el prefijo de la clave |
| **Requisito** | Empresa con plan Premium (o los 14 días de prueba gratuita) — ver [precios](https://asistentefacturaelectronica.com/ayuda/api/precios) |
| **Contacto** | [contacto@nanofactura.com](mailto:contacto@nanofactura.com) |
Todas las rutas cuelgan de `/:id-empresa`, el código de tu empresa. Si tu empresa es `mi-empresa`, emitís contra `https://nanofactura.com/api/mi-empresa/invoices`.
¿Dudas antes de arrancar? Mirá las **[preguntas frecuentes](https://asistentefacturaelectronica.com/ayuda/api/preguntas-frecuentes)**.
---
# Límites, cuotas y buenas prácticas
> Límite de peticiones, cuota mensual de comprobantes, tiempos máximos de respuesta y cómo emitir facturación electrónica en volumen sin errores.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/limites
## Límite de peticiones
**200 peticiones por minuto por empresa.** Se cuenta por `id-empresa`, todas las rutas `/:id-empresa/…` comparten ese cupo sin importar desde cuántos servidores salgan.
> **El cupo lo comparten tus integraciones y la app web**
>
> Los usuarios de tu empresa trabajando en la aplicación consumen del mismo cupo. En la práctica no molesta —200 por minuto es mucho— pero tenelo en cuenta si vas a hacer una carga grande en horario de trabajo.
Al superarlo:
```json
{ "error": "Too many requests. Please try again later." }
```
Es transitorio: esperá unos segundos y reintentá.
## Cuota mensual de comprobantes
Tu plan incluye una cantidad **mensual** de comprobantes —250 en Premium— que podés aumentar según tu necesidad; ver [precios](https://asistentefacturaelectronica.com/ayuda/api/precios). Cada emisión te devuelve el estado del cupo:
```json
{
"quota": { "granted": 500, "used": 138, "remaining": 362, "periodEnd": "2026-09-16" }
}
```
Tres reglas:
- **Es mensual y no se acumula.** Lo que no emitiste este mes no engorda el siguiente.
- **Homologación consume el mismo cupo.**
- **`quota` puede venir en `null`** — en reintentos idempotentes, que no consumen. Tu cliente tiene que tolerarlo: `null` no es "cero disponible", es "no hay nada que informar".
Al agotarse, la emisión responde `429 SUBSCRIPTION_QUOTA_EXCEEDED`. Al 90% del cupo la cuenta recibe un aviso por email.
> **Monitoreá `quota.remaining`**
>
> Está en cada respuesta de emisión, gratis. Guardalo y alertá cuando baje de un umbral tuyo: es la forma de enterarte antes de que un lote de facturación se frene a la mitad.
Al superarlas se responde `429 QUOTA_EXCEEDED`. La ventana es **móvil** y el intento rechazado no cuenta, así que se liberan solas.
Los números son holgados para el uso normal. Si tu integración los toca, casi seguro está haciendo en bucle algo que debería hacer una vez.
## Tiempos máximos
| Ruta | Techo |
| :--- | :---: |
| General | **15 s** |
| `/arca/*` | **25 s** |
| Descarga de PDF | **25 s** |
Al agotarse se responde `504`. **Un `504` en una emisión no significa que el comprobante no se haya emitido**: reintentá con la misma `idempotency`. Ver [errores](https://asistentefacturaelectronica.com/ayuda/api/errores#504--se-agotó-el-tiempo).
Configurá el timeout de **tu** cliente HTTP por encima de estos valores (30 segundos es un buen número). Un timeout de 10 segundos del lado tuyo corta emisiones que iban a responder bien, y te deja sin saber si el comprobante existe.
## Emitir en volumen
ARCA exige numeración consecutiva por punto de venta y tipo, así que la API emite **de a un comprobante por vez para cada combinación `posNumber` + `voucherType`**.
> **Secuenciá, no paralelices**
>
> Lanzar en paralelo comprobantes del **mismo** punto de venta y tipo **no acelera nada** —igual se serializan— y sólo produce errores `429 VOUCHER_LOCK_TIMEOUT`.
>
> Distintos puntos de venta **sí** pueden emitir en paralelo, porque cada uno lleva su propia numeración.
```js title="Correcto: secuencial por punto de venta"
for (const venta of ventas) {
const comprobante = await emitir(venta) // uno tras otro
await guardar(venta.id, comprobante)
}
```
```js title="Incorrecto: paralelo sobre el mismo punto de venta"
await Promise.all(ventas.map(emitir)) // → 429 en cascada
```
Si tenés que emitir mucho y rápido, la forma de paralelizar es **por punto de venta**: agrupá las ventas por `posNumber` y corré un flujo secuencial por grupo.
### Lista de control para un lote
1. **Generá y guardá la `idempotency` antes de emitir**, junto a la venta. Si el proceso se cae a la mitad, al reanudarlo reusás la misma clave y no duplicás nada.
2. **Guardá el resultado inmediatamente** después de cada comprobante, no al final del lote.
3. **No pidas el PDF durante el lote.** Se genera en segundo plano; pedilo cuando alguien lo necesite.
4. **Cortá al primer error permanente** (`400`, `422`): si el primero está mal armado, probablemente lo estén todos.
5. **Registrá `arcaObservations`** cuando no venga `null`: son comprobantes aprobados, pero con algo que ARCA quiso señalar.
## Recomendaciones generales
| | |
| :--- | :--- |
| **Guardá los identificadores, no los documentos** | `id`, `cae`, `voucherNumber`, `posNumber`, `voucherType`. El PDF lo pedís cuando haga falta. |
| **Usá `externalReference`** | Es lo que ata el comprobante a tu operación y lo hace buscable después. |
| **Mandá `customer.stateId`** | El único dato estructurado de ubicación. Sin él no hay reportes por jurisdicción, y no se puede reconstruir. |
| **Consultá el padrón** | Te asegura la condición fiscal correcta y evita rechazos de ARCA. |
| **Una API Key por sistema** | Cada comprobante guarda cuál lo emitió. |
| **Chequeá `ok`, no el status** | Todas las respuestas traen `ok`; los errores de negocio traen además `code`. |
| **Probá en homologación** | Cambiar a producción es cambiar una variable de entorno. |
---
# PDF del comprobante y envío por email
> Cómo obtener el PDF de una factura electrónica emitida por API, en qué momento pedirlo, y cómo enviarlo automáticamente por email al cliente sin programar plantillas.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/pdf-y-email
El PDF se genera automáticamente para cada comprobante **pero** se genera **en segundo plano**, unos segundos después. La respuesta del `POST` te da el CAE de inmediato; el archivo llega poco después.
> **No guardes el PDF en tu sistema**
>
> Pedilo **en el momento en que lo necesitás** —cuando el usuario lo abre, lo imprime o lo adjunta—.
>
> Si lo guardás, te obliga a resolver almacenamiento, backups, permisos de acceso, retención y de eso nos encargamos nosotros, estarías duplicando esfuerzo y costos. Lo que sí conviene guardar de la emisión son los identificadores: **`id`, `cae`, `voucherNumber`, `posNumber` y `voucherType`**. Con el `id` traés el PDF cuando haga falta, tantas veces como haga falta.
## Obtener el PDF
```http
GET /:id-empresa/invoices/:id/pdf?versions=1,2,3
```
`versions` selecciona las copias a estampar: `1` original, `2` duplicado, `3` triplicado. Por defecto `1`. Se devuelven ordenadas.
```bash
curl "https://nanofactura.com/api/mi-empresa/invoices/14805/pdf?versions=1" \
-H "Authorization: Bearer PROD_tu_api_key" \
-o factura.pdf
```
Respuesta: `application/pdf` con `Content-Disposition: attachment`. El nombre del archivo sigue el formato `{cuit}_{voucherType}_{posNumber}_{voucherNumber}.pdf` — por ejemplo `20000000000_11_00001_00000045.pdf`.
### Si todavía no está
```json
{ "ok": false, "code": "PDF_NOT_READY", "error": "El PDF del comprobante todavía se está generando" }
```
Es `409` y es **transitorio**: reintentá en unos segundos. Sólo lo vas a ver si pedís el PDF inmediatamente después de emitir — que es justamente lo que conviene no hacer.
> **Cómo servirlo a tu usuario**
>
> No descargues el PDF al emitir "por las dudas". Poné un botón *Descargar factura* que llame a tu backend, y que tu backend llame a este endpoint en ese momento y transmita el archivo. Es una línea de código y te ahorra todo el almacenamiento.
## Enviarlo por email
La API manda el comprobante por correo, con la plantilla y las copias que corresponden. **No lo bajes para reenviarlo vos.**
### Al emitir
Es la forma recomendada: el envío sale solo, en segundo plano, sin bloquear la respuesta.
```json
{
"emails": [
{
"email": "cliente@example.com",
"name": "Pedro López",
"comments": "¡Gracias por su compra!",
"pdfVersions": [1]
}
]
}
```
| Campo | Obligatorio | Descripción |
| :--- | :---: | :--- |
| `email` | **Sí** | Dirección del destinatario. |
| `name` | No | Nombre del destinatario. |
| `comments` | No | Texto a incluir en el cuerpo del correo. |
| `pdfVersions` | No | Copias a adjuntar. Por defecto `[1]`. |
### Enviar después de emitir
```http
POST /:id-empresa/invoices/:id/emails
```
```json
{
"email": "contador@example.com",
"name": "Estudio Contable",
"comments": "Copia para el estudio",
"pdfVersions": [1, 2]
}
```
Responde `201` con la cantidad acumulada de destinatarios. Requiere que el PDF ya esté generado (si no, `409`).
> **Máximo 3 destinatarios por comprobante**
>
> Cuenta los de la emisión más los agregados después. Es suficiente para el caso real —el cliente, su contador y una copia interna— y evita que la API se use como lista de distribución.
> **En la emisión, rechaza el comprobante entero**
>
> Si mandás `emails` con una dirección no válida a `POST /invoices`, la validación falla **antes** de llamar a ARCA: no se emite nada. Asegurate de enviar direcciones de email válidas.
## Regenerar el PDF
```http
POST /:id-empresa/invoices/:id/pdf/regenerate
```
Vuelve a generar el archivo y reemplaza el anterior. **No reenvía correos**.
Es una **reparación**, para cuando la generación falló o el archivo salió mal. Tiene dos topes:
- **Hasta 3 veces por comprobante.**
- **Sólo dentro de los 90 días de emitido.**
Pasado ese plazo el PDF es un documento cerrado: la plantilla, el logo del punto de venta y hasta tus datos fiscales pudieron cambiar, así que regenerarlo devolvería un archivo distinto del que entregaste en su momento. Se responde `409`:
```json
{
"ok": false,
"code": "VALIDATION_ERROR",
"error": "El comprobante se emitió hace 124 días y sólo se puede regenerar el PDF dentro de los 90 días de emitido. Descargalo con GET /invoices/1485/pdf."
}
```
**Descargar el PDF sigue funcionando siempre**, sin límite de tiempo ni de cantidad. El tope es sólo para regenerarlo.
## Personalizar el comprobante
El diseño no se configura por API: sale del **punto de venta** con el que emitís. Nombre de fantasía, logo, domicilio y leyenda se cargan una vez y se aplican a todos sus comprobantes. Ver [Puntos de venta](https://asistentefacturaelectronica.com/ayuda/api/puntos-de-venta).
---
# Precios de la API
> Cuánto cuesta emitir facturación electrónica de ARCA por API - qué incluye Premium, cuántos comprobantes vienen incluidos y el precio por comprobante adicional.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/precios
La API está incluida en **Premium**. No se contrata aparte ni tiene un abono propio: la misma suscripción que habilita la empresa en la aplicación habilita la API.
## Cuánto cuesta
| | |
| :--- | :--- |
| **Premium** | **$4.000 / mes** por empresa, IVA incluido |
| **Comprobantes incluidos** | **250 por mes** |
| **Comprobante adicional** | **$12** cada uno |
Precios vigentes desde el **1 de septiembre de 2026**, en pesos argentinos.
La suscripción es **por empresa (CUIT)**, no por cuenta: si facturás para varios CUIT, cada uno lleva la suya, con su propio ciclo y su propia cuota.
## Cómo funcionan los comprobantes incluidos
Los 250 comprobantes son un cupo **mensual**, y aplican a todo lo que emita la empresa: da lo mismo si el comprobante salió por la API, desde la aplicación o desde la extensión en «comprobantes en línea».
- **No se acumulan.** Lo que no emitiste este mes no engorda el siguiente.
- **Homologación consume del mismo cupo.** Las pruebas cuentan.
- **Los reintentos idempotentes no consumen.** Reintentar una emisión con la misma `idempotency` no descuenta de nuevo.
Cada emisión te devuelve el estado del cupo en la respuesta:
```json
{
"quota": { "granted": 500, "used": 138, "remaining": 362, "periodEnd": "2026-09-16" }
}
```
Ver [Límites y buenas prácticas](https://asistentefacturaelectronica.com/ayuda/api/limites) para el detalle del campo y qué hacer al agotarse.
## Si necesitás más de 250
Ampliás la cuota mensual desde tu [cuenta de facturación](https://asistentefacturaelectronica.com/app/administrar/cuentas-facturacion). El nuevo cupo pasa a ser el total mensual: se cobra junto con la suscripción y queda vigente mientras la mantengas.
Conviene ampliar **antes** de agotar el cupo: al llegar a cero la emisión responde `429 SUBSCRIPTION_QUOTA_EXCEEDED` y se frena. Al 90% del cupo la cuenta recibe un aviso por email, y `quota.remaining` viene en cada respuesta para que puedas alertar con tu propio umbral.
## Lo que no se cobra aparte
- **Consultas.** Traer un comprobante ya emitido, listar, consultar el padrón de ARCA o pedir el estado de los servicios no consume cuota.
- **PDF y envío por email.** El PDF del comprobante se descarga sin costo adicional y lo puedes enviar por email hasta 3 destinatarios.
- **Entorno de homologación.** consume cuota de comprobantes.
- **API Keys, certificados y puntos de venta.** Sin límite de cantidad ni costo por unidad.
## Querés probar primero?
Premium tiene **14 días de prueba** en la primera empresa que crees, sin tarjeta. Alcanzan de sobra para dejar la integración andando en homologación y verificar la emisión real.
También podés recorrer toda la documentación y probar contra homologación sin compromiso.
## Cómo se paga
Débito automático mensual con tarjeta de crédito por Mercado Pago, link de pago (tarjeta o dinero en cuenta) o transferencia bancaria. El pago es por adelantado del período. Sin mínimo de permanencia: cancelás cuando quieras y seguís usando el período contratado.
Disponible solo en Argentina. El detalle completo de planes está en la **[página de precios](https://asistentefacturaelectronica.com/#precios)**.
---
# Preguntas frecuentes sobre la API de facturación electrónica
> Cuánto cuesta, para quién está pensada, con qué lenguajes se integra y por qué conviene usar la API de NanoFactura en vez de armar una integración propia con ARCA.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/preguntas-frecuentes
## ¿Cuánto cuesta usar la API?
La API está incluida en el plan **Premium**. No se cobra aparte ni por llamada: el plan incluye 250 comprobantes por mes, y podés ampliarlos por $12 cada uno si necesitás más. El detalle está en **[Precios](https://asistentefacturaelectronica.com/ayuda/api/precios)**.
Podés arrancar con la **prueba gratuita de 14 días** y probar la integración completa.
👉 **[Creá tu empresa](https://asistentefacturaelectronica.com/app/suscribir-empresa)**
Podés probar con el entorno de **homologación**, donde los comprobantes no tienen validez fiscal y podés equivocarte sin consecuencias. Luego pasás a producción.
## ¿Por qué usar esta API en vez de integrarme directamente con ARCA?
Porque la parte difícil no es la primera factura: es todo lo que viene después.
Una integración propia funciona el día uno y empieza a costar el día treinta. Lo que tenés que resolver y mantener vos:
| | Integración propia | Con NanoFactura |
| :--- | :--- | :--- |
| **Cambios de normativa** | Cada resolución de ARCA es un cambio en tu código, con fecha límite | Lo actualizamos nosotros. **Tu código no cambia** |
| **Certificados y firma** | Renovar, firmar, cachear y renovar tokens de autenticación | Automático |
| **Casos borde** | Notas de crédito, moneda extranjera, percepciones, tributos, comprobantes asociados, cada uno con sus reglas | Un campo del JSON |
| **Numeración** | Llevar contadores correlativos por punto de venta y tipo, manejar concurrencia, problemas de red y de ARCA | Lo resuelve la API |
| **Cortes de red** | Si se corta al pedir el CAE, no sabés si el comprobante existe. Reconciliarlo es delicado y hay que hacerlo bien | Reintentás con la misma clave de idempotencia |
| **Redondeo de importes** | ARCA rechaza el comprobante si los totales no cierran al centavo | Los calcula la API |
| **Almacenar comprobantes** | Base de datos, backups, retención, y responder consultas históricas | Consulta por API o desde la app |
| **PDF** | Diseñar la plantilla, generar el archivo, guardarlo, servirlo | Se genera solo |
| **Envío al cliente** | Servidor de correo, plantillas, entregabilidad, rebotes | Un campo del JSON. Desde la app ves los rebotes. |
Ese trabajo no se hace una vez: se mantiene para siempre, y las fechas las pone ARCA, no tu equipo. La pregunta no es si podés hacerlo —claro que podés—, es si **querés que ese sea tu problema**.
## ¿Para quién está pensada la API?
Para empresas, SaaS y sistemas que **ya tienen dónde ocurre la venta** y no quieren que alguien la vuelva a cargar a mano en otro lado.
Va bien si:
- Tenés un **e-commerce, ERP, sistema de gestión o SaaS** y querés que la factura salga sola cuando se concreta la venta.
- Facturás **volumen** y cargar los comprobantes a mano ya no es viable.
- Querés que tu producto **facture en nombre de tus clientes**, sin construir facturación electrónica desde cero.
- Necesitás que la facturación sea **confiable y auditable**, no un paso manual que alguien puede olvidarse.
Lo que te ahorrás: tablas nuevas en tu base de datos, pantallas de consulta, almacenamiento de PDFs, plantillas de email, y la mitad de un equipo mirando cuándo cambia la normativa.
Si en cambio facturás poco y a mano, probablemente te convenga la **[extensión de Chrome](https://asistentefacturaelectronica.com/ayuda/)**, que automatiza la carga en *Comprobantes en Línea* sin que programes nada.
## ¿Con qué lenguajes de programación me puedo integrar?
Con **cualquiera**. Es una API REST sobre HTTP con JSON: si tu lenguaje puede hacer un `POST`, puede facturar.
Tenemos ejemplos completos en **[JavaScript, PHP y Python](https://asistentefacturaelectronica.com/ayuda/api/ejemplos)**, pero funciona igual desde C#, Java, Go, Ruby, Kotlin, Rust o lo que uses.
También desde plataformas que no son un lenguaje:
- **Google Apps Script** — facturar desde una hoja de cálculo.
- **n8n, Make, Zapier** y otras herramientas **no-code**: alcanza con un nodo de petición HTTP.
- Clientes como **Postman** o **Insomnia**, para probar antes de escribir nada.
No hay SDK obligatorio ni librería que instalar. Los ejemplos son eso: ejemplos.
## ¿Dónde veo los comprobantes generados?
De dos formas:
1. **Por API**, con [`GET /:id-empresa/invoices`](https://asistentefacturaelectronica.com/ayuda/api/consultar-comprobantes) — con filtros por fecha, cliente, importe o tu propia referencia externa.
2. **En la app web**: `https://asistentefacturaelectronica.com/app/comprobantes/`
Los comprobantes que emitís por API y los que emitís desde la app aparecen en el mismo listado. El campo `source` te dice de dónde vino cada uno.
> **Los comprobantes de homologación no se ven por defecto**
>
> Si emitiste con una clave `TEST_`, tenés que activar la **vista de homologación** en el listado de la app. Sin eso sólo se muestran los comprobantes con validez fiscal, y vas a creer que la emisión no funcionó.
## ¿Los comprobantes tienen validez fiscal?
Sí, los emitidos con una clave `PROD_`. Son comprobantes electrónicos autorizados por ARCA, con su CAE, tal como si emitirías desde *Comprobantes en Línea*.
Los emitidos con una clave `TEST_` van contra el entorno de **homologación** de ARCA y **no** tienen validez fiscal: son para probar.
## ¿Necesito mi propio certificado de ARCA?
Sí. El certificado digital es lo que autoriza a facturar en tu nombre y **es tuyo**, tramitado con tu Clave Fiscal. Es un trámite de una sola vez por entorno, y tiene validez dos años.
La app te genera el pedido de certificado y te guía en el trámite: ver **[Vincular tu certificado](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca)**.
## ¿Puedo usar la API y la extensión de Chrome a la vez?
Sí. Comparten la misma cuenta, empresa y la misma suscripción.
Es habitual: la API factura las ventas del sistema, y la extensión cubre lo que se factura a mano. Tené en cuenta que ARCA define **puntos de venta distintos** para cada canal, es decir que tenés que crear un punto de venta del tipo webservice para facturar con la API.
## ¿Qué pasa si ARCA se cae?
La emisión responde `502 ARCA_CONNECTION_ERROR` y el comprobante no se emite. Es transitorio: reintentá con backoff, y **con la misma clave de idempotencia** para no duplicar nada si el pedido llegó a pasar.
Para saber si el problema es de ARCA o tuyo, consultá [`GET /:id-empresa/arca/dummy`](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca#estado-de-los-servidores): te dice si los servidores de ARCA están arriba.
## ¿Puedo anular una factura ya emitida?
No se puede borrar ni modificar un comprobante autorizado — eso es así por diseño fiscal, no una limitación nuestra. Lo que se hace es emitir una **nota de crédito** que lo compensa. Ver [cómo](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante#notas-de-crédito-y-débito).
## ¿Cuántas empresas puedo facturar con una misma cuenta?
Las que quieras, cada una con su plan. Cada empresa tiene su propio `id-empresa`, su certificado, sus puntos de venta y sus API Keys. Podés hacer un solo pago para todas tus empresas vinculándolas a la misma cuenta de facturación.
Una API Key opera **sólo** sobre la empresa que aparece en su URL. Si facturás para varias, necesitás una clave por empresa.
## ¿Tienen soporte?
Sí. Escribinos a **[contacto@nanofactura.com](mailto:contacto@nanofactura.com)**.
Si tu consulta es sobre una emisión que falló, mandanos el `code`, el `error` completo y —si lo hay— el contenido de `details`: con eso lo resolvemos mucho más rápido.
---
¿No encontraste tu pregunta? Escribinos a [contacto@nanofactura.com](mailto:contacto@nanofactura.com) o empezá por **[Primeros pasos](https://asistentefacturaelectronica.com/ayuda/api/primeros-pasos)**.
---
# Primeros pasos con la API de facturación electrónica
> Emití tu primera factura electrónica de ARCA por API en cinco minutos. Guía rápida con ejemplos en curl, JavaScript, PHP y Python, desde la verificación de la API key hasta el CAE.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/primeros-pasos
En esta guía emitís una factura de prueba y obtenés un CAE real de **homologación** de ARCA. Sirve para verificar que toda la cadena funciona antes de escribir una línea de tu integración.
## Antes de empezar
Necesitás tres cosas, todas de una sola vez:
1. Un **certificado de ARCA** activo → [cómo obtenerlo](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca)
2. Al menos un **punto de venta** habilitado → [cómo configurarlo](https://asistentefacturaelectronica.com/ayuda/api/puntos-de-venta)
3. Una **API Key** con prefijo `TEST_` → [cómo crearla](https://asistentefacturaelectronica.com/ayuda/api/api-keys)
Anotá también tu **`id-empresa`**: es el código de tu empresa y va en todas las URLs. Lo ves en la barra de direcciones de la app, en `…/app/administrar/`.
> **Empezá siempre por homologación**
>
> Una clave `TEST_` factura contra el entorno de pruebas de ARCA. Los comprobantes **no tienen validez fiscal** y podés equivocarte todo lo que haga falta. Cuando funcione, cambiás la clave por una `PROD_` y no tocás nada más del código.
## Paso 1 — Verificá la conexión
Es la primera llamada que conviene hacer siempre: valida la clave y te dice si la empresa tiene lo mínimo para facturar.
```bash
curl https://nanofactura.com/api/mi-empresa/api-keys/test \
-H "Authorization: Bearer TEST_tu_api_key"
```
```json
{ "ok": true, "environment": "homologation", "certificates": true, "pos": true }
```
Si `certificates` o `pos` vienen en `false`, falta configuración: volvé a los pasos de arriba. Si recibís `401`, la clave está mal copiada o fue revocada.
## Paso 2 — Emití la factura
Este ejemplo es una **Factura C** (la que emite un monotributista) a consumidor final. Si sos responsable inscripto, mirá [qué comprobante te corresponde](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante). Reemplazá los valores al momento de probar.
**curl**
```bash
curl -X POST https://nanofactura.com/api/mi-empresa/invoices \
-H "Authorization: Bearer TEST_tu_api_key" \
-H "Content-Type: application/json" \
-d '{
"idempotency": "prueba-0001",
"posNumber": 1,
"voucherType": 11,
"concept": 1,
"voucherDate": "20260811",
"customer": {
"docType": 96,
"docNumber": "35888999",
"name": "Pedro López",
"address": "Av. de Mayo 500, CABA",
"stateId": 0,
"taxType": 5
},
"items": [
{ "description": "Producto de prueba", "quantity": 2, "price": 15000 }
]
}'
```
**JavaScript (Node.js 18+, sin dependencias)**
```js
const res = await fetch('https://nanofactura.com/api/mi-empresa/invoices', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NANOFACTURA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
idempotency: crypto.randomUUID(),
posNumber: 1,
voucherType: 11,
concept: 1,
voucherDate: '20260811',
customer: {
docType: 96,
docNumber: '35888999',
name: 'Pedro López',
address: 'Av. de Mayo 500, CABA',
stateId: 0,
taxType: 5,
},
items: [{ description: 'Producto de prueba', quantity: 2, price: 15000 }],
}),
})
const data = await res.json()
if (!data.ok) throw new Error(`${data.code}: ${data.error}`)
console.log('CAE:', data.result.cae)
```
**PHP (8.1+, con cURL)**
```php
bin2hex(random_bytes(16)),
'posNumber' => 1,
'voucherType' => 11,
'concept' => 1,
'voucherDate' => '20260811',
'customer' => [
'docType' => 96,
'docNumber' => '35888999',
'name' => 'Pedro López',
'address' => 'Av. de Mayo 500, CABA',
'stateId' => 0,
'taxType' => 5,
],
'items' => [
['description' => 'Producto de prueba', 'quantity' => 2, 'price' => 15000],
],
];
$ch = curl_init('https://nanofactura.com/api/mi-empresa/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NANOFACTURA_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!($data['ok'] ?? false)) {
throw new RuntimeException("{$data['code']}: {$data['error']}");
}
echo "CAE: {$data['result']['cae']}\n";
```
**Python (3.9+, con requests)**
```python
import os
import uuid
import requests
payload = {
"idempotency": str(uuid.uuid4()),
"posNumber": 1,
"voucherType": 11,
"concept": 1,
"voucherDate": "20260811",
"customer": {
"docType": 96,
"docNumber": "35888999",
"name": "Pedro López",
"address": "Av. de Mayo 500, CABA",
"stateId": 0,
"taxType": 5,
},
"items": [
{"description": "Producto de prueba", "quantity": 2, "price": 15000}
],
}
response = requests.post(
"https://nanofactura.com/api/mi-empresa/invoices",
headers={"Authorization": f"Bearer {os.environ['NANOFACTURA_API_KEY']}"},
json=payload,
timeout=30,
)
data = response.json()
if not data.get("ok"):
raise RuntimeError(f"{data.get('code')}: {data.get('error')}")
print("CAE:", data["result"]["cae"])
```
La respuesta:
```json
{
"ok": true,
"result": {
"id": 1485,
"voucherNumber": 45,
"cae": "76251489623547",
"totalAmount": 30000,
"arcaObservations": null,
"idempotencyHit": false
},
"quota": { "type": "invoices", "granted": 500, "used": 138, "remaining": 362 }
}
```
Ya está: ese `cae` lo otorgó ARCA. Guardá `id`, `cae`, `voucherNumber`, `posNumber` y `voucherType` contra tu operación — es lo único que necesitás conservar.
## Paso 3 — Mirá el comprobante
Por API, con [`GET /invoices`](https://asistentefacturaelectronica.com/ayuda/api/consultar-comprobantes), o en la app web:
**`https://asistentefacturaelectronica.com/app/comprobantes/`**
> **Los comprobantes de homologación no se ven por defecto**
>
> Si emitiste con una clave `TEST_`, activá la **vista de homologación** en el listado de la app. Sin eso el listado muestra únicamente los comprobantes fiscales y vas a creer que no se emitió nada.
## Paso 4 — El PDF
El PDF se genera **en segundo plano**, unos segundos después de la emisión:
```bash
curl "https://nanofactura.com/api/mi-empresa/invoices/1485/pdf?versions=1" \
-H "Authorization: Bearer TEST_tu_api_key" \
-o factura.pdf
```
> **No lo guardes en tu sistema luego de generar**
>
> Pedilo en el momento en que alguien lo abre o lo imprime. Con el `id` lo traés siempre que lo necesites. Y si lo que querés es que le llegue a tu cliente, no lo bajes para reenviarlo: mandá los destinatarios en `emails` al emitir. Ver [PDF y envío por email](https://asistentefacturaelectronica.com/ayuda/api/pdf-y-email).
## Y ahora
- **[Emisión de comprobantes](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante)** — todos los campos, en detalle.
- **[Equivalencia de campos con ARCA](https://asistentefacturaelectronica.com/ayuda/api/campos-arca)** — cómo se llama cada cosa en ARCA y cuándo es obligatoria.
- **[Ejemplos completos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos)** — repositorio con implementaciones listas en JavaScript, PHP y Python.
- **[Errores](https://asistentefacturaelectronica.com/ayuda/api/errores)** — qué reintentar y qué corregir.
## Pasar a producción
Cuando la integración funcione en homologación:
1. Verificá que tengas un **certificado de producción** activo ([guía](https://asistentefacturaelectronica.com/ayuda/api/certificado-arca)).
2. Creá una API Key **`PROD_`** desde la app.
3. Cambiá el valor de la variable de entorno. **Nada más.**
El código no cambia: el entorno lo decide el prefijo de la clave, no un parámetro del pedido. Confirmá con `GET /api-keys/test` que responda `"environment": "production"` y emitiste tu primer comprobante fiscal.
---
# Códigos de alícuota de IVA de ARCA
> Tabla de códigos de alícuota de IVA de ARCA (AFIP) para facturación electrónica: 21%, 10,5%, 27%, 5%, 2,5%, exento y no gravado, con su efecto en los totales del comprobante.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/alicuotas-iva
Campo **`items[].taxType`** · en ARCA: **`Iva[].Id`**
| Código | Alícuota | Tratamiento | Va a |
| :---: | :---: | :--- | :--- |
| **1** | — | No gravado | `ImpTotConc` |
| **2** | — | Exento | `ImpOpEx` |
| **3** | 0% | Exento | `ImpOpEx` |
| **4** | 10,5% | Gravado | `ImpNeto` + IVA |
| **5** | **21%** | Gravado | `ImpNeto` + IVA |
| **6** | 27% | Gravado | `ImpNeto` + IVA |
| **8** | 5% | Gravado | `ImpNeto` + IVA |
| **9** | 2,5% | Gravado | `ImpNeto` + IVA |
El **21%** (`5`) es la alícuota general y la que corresponde en la mayoría de los casos.
Cualquier código no listado se trata como **no gravado** e incrementa `ImpTotConc`.
> **No confundir con la condición de IVA del cliente**
>
> `items[].taxType` es la **alícuota de la línea**. `customer.taxType` es la **condición del receptor frente al IVA**, y usa [otra tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/condiciones-iva) con códigos que se solapan.
>
> `taxType: 5` en un ítem es 21%. `taxType: 5` en el cliente es Consumidor Final. Son campos distintos: es el error más común al integrar, y el que las [constantes del repositorio](https://asistentefacturaelectronica.com/ayuda/api/ejemplos) hacen imposible.
## Qué mandar si sos monotributista
Nada: en los comprobantes **C** (`11`, `12`, `13`) los ítems **no llevan `taxType`**. Todas las líneas van al neto y no se informa IVA, así que no hay alícuota que declarar.
Si tu sistema lo manda igual —porque comparte el armado del ítem con las facturas A y B—, se acepta y se ignora.
## Cómo se calcula
Por cada ítem, con el neto ya calculado (`cantidad × precio − descuento`):
- **Gravado** (`4`, `5`, `6`, `8`, `9`) → el neto va a `ImpNeto` y se calcula `IVA = neto × alícuota`.
- **Exento** (`2`, `3`) → el neto va a `ImpOpEx`. No genera IVA.
- **No gravado** (`1` u otro) → el neto va a `ImpTotConc`. No genera IVA.
Detalle completo: [cálculo de totales](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante#cálculo-de-totales).
## En JavaScript, PHP y Python
```js
ALICUOTA_IVA.VEINTIUNO // 5 → 21%
ALICUOTA_IVA.DIEZ_CINCO // 4 → 10,5%
ALICUOTA_IVA.CERO // 3 → 0%
ALICUOTA_IVA.EXENTO // 2 → exento
ALICUOTA_IVA.NO_GRAVADO // 1
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de condición frente al IVA de ARCA
> Tabla de códigos de condición de IVA del receptor de ARCA (AFIP): responsable inscripto, monotributo, consumidor final, exento y el resto, para el campo CondicionIVAReceptorId.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/condiciones-iva
Campos **`customer.taxType`** y **`receiverTaxConditionId`** · en ARCA: **`CondicionIVAReceptorId`**
| Código | Condición |
| :---: | :--- |
| **1** | IVA Responsable Inscripto |
| **3** | IVA No Responsable |
| **4** | IVA Sujeto Exento |
| **5** | **Consumidor Final** |
| **6** | **Responsable Monotributo** |
| **7** | Sujeto No Categorizado |
| **8** | Proveedor del Exterior |
| **9** | Cliente del Exterior |
| **10** | IVA Liberado – Ley Nº 19.640 |
| **11** | IVA Responsable Inscripto – Agente de Percepción |
| **12** | Pequeño Contribuyente Eventual |
| **13** | Monotributista Social |
| **14** | Pequeño Contribuyente Eventual Social |
| **15** | IVA No Alcanzado |
Los más usados son **`5`** (consumidor final), **`6`** (monotributo), **`1`** (responsable inscripto) y **`4`** (exento).
> **El código 2 está derogado**
>
> `2` (Responsable no Inscripto) ya no se usa. No lo mandes.
## Qué comprobante corresponde según la condición
Si sos **Responsable Inscripto**, la condición de tu cliente decide la letra:
| Condición del receptor | Emitís |
| :--- | :--- |
| `1` IVA Responsable Inscripto | **Factura A** |
| `6` Responsable Monotributo | **Factura A** |
| `4` IVA Sujeto Exento | **Factura B** |
| `5` Consumidor Final | **Factura B** |
> **Al monotributista le corresponde Factura A**
>
> Es el error más frecuente al integrar, porque durante años fue Factura B. Hoy un responsable inscripto que le vende a un monotributista emite **Factura A**, con el IVA discriminado.
Si sos **monotributista o exento**, siempre **Factura C**, sea cual sea la condición del receptor.
Detalle: [Qué comprobante emitir](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante).
> **No confundir con la alícuota de IVA del ítem**
>
> `customer.taxType` es la **condición del receptor**. `items[].taxType` es la **alícuota de la línea**, y usa [otra tabla](https://asistentefacturaelectronica.com/ayuda/api/tablas/alicuotas-iva).
>
> `5` como condición es Consumidor Final. `5` como alícuota es 21%.
## Cómo saber la condición de un cliente
Consultá el padrón de ARCA:
```http
GET /:id-empresa/arca/padron/30703088534
```
Devuelve `taxCondition`, que se usa **tal cual** como `customer.taxType`. Ver [Consultas a ARCA](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca).
Y para ver qué condiciones acepta ARCA en cada clase de comprobante:
```http
GET /:id-empresa/arca/condicion-iva-receptor?claseCmp=A
```
## En JavaScript, PHP y Python
```js
CONDICION_IVA.RESPONSABLE_INSCRIPTO // 1
CONDICION_IVA.EXENTO // 4
CONDICION_IVA.CONSUMIDOR_FINAL // 5
CONDICION_IVA.MONOTRIBUTO // 6
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de moneda de ARCA
> Tabla de códigos de moneda de ARCA (AFIP) para facturación electrónica en dólares, euros y otras divisas, y cómo informar la cotización del día.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/monedas
Campo **`currency.id`** · en ARCA: **`MonId`** · por defecto **`PES`**
| Código | Moneda |
| :---: | :--- |
| **PES** | **Pesos argentinos** *(por defecto)* |
| **DOL** | Dólar estadounidense |
| **060** | Euro |
| **012** | Real |
| **011** | Peso uruguayo |
| **021** | Libra esterlina |
| **064** | Yuan |
Se admite el catálogo completo de ARCA; éstas son las de uso frecuente.
## Facturar en moneda extranjera
```json
{
"currency": { "id": "DOL", "quotation": 1350.50 },
"items": [
{ "description": "Licencia anual", "quantity": 1, "price": 1200, "taxType": 5 }
]
}
```
> **La cotización es obligatoria y no se consulta sola**
>
> Si `currency.id` no es `PES`, **tenés que mandar `currency.quotation`** con la cotización del día contra el peso argentino. La API no la busca por vos: el tipo de cambio que corresponde aplicar es una decisión tuya, no un dato que se pueda adivinar.
Los `price` de los ítems van **en la moneda del comprobante** (en el ejemplo, 1200 dólares), no convertidos a pesos. La conversión la hace ARCA con la cotización que informás.
## Cancelación en la misma moneda
```json
{
"currency": { "id": "DOL", "quotation": 1350.50 },
"cancelsInSameForeignCurrency": "S"
}
```
`cancelsInSameForeignCurrency` (en ARCA, `CanMisMonExt`) acepta `'S'` o `'N'` e indica si la operación se cancela en la misma moneda extranjera. Sólo aplica cuando la moneda no es `PES`.
## En JavaScript, PHP y Python
```js
MONEDA.PESOS // 'PES'
MONEDA.DOLAR // 'DOL'
MONEDA.EURO // '060'
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de provincia de ARCA
> Tabla de códigos de provincia de ARCA (AFIP) para el campo stateId del receptor. Las 23 provincias argentinas más CABA, para reportes por jurisdicción e Ingresos Brutos.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/provincias
Campo **`customer.stateId`** · no se envía a ARCA, se guarda con el comprobante
Son **24 jurisdicciones**: las 23 provincias argentinas más la **Ciudad Autónoma de Buenos Aires**, que no es una provincia sino un distrito autónomo, pero cuenta como jurisdicción propia a efectos de Ingresos Brutos y del Convenio Multilateral. ARCA le asigna el código `0`.
| Código | Jurisdicción | | Código | Jurisdicción |
| :---: | :--- | :--- | :---: | :--- |
| **0** | Ciudad Autónoma de Buenos Aires *(distrito)* | | **13** | Santiago del Estero |
| **1** | Buenos Aires | | **14** | Tucumán |
| **2** | Catamarca | | **16** | Chaco |
| **3** | Córdoba | | **17** | Chubut |
| **4** | Corrientes | | **18** | Formosa |
| **5** | Entre Ríos | | **19** | Misiones |
| **6** | Jujuy | | **20** | Neuquén |
| **7** | Mendoza | | **21** | La Pampa |
| **8** | La Rioja | | **22** | Río Negro |
| **9** | Salta | | **23** | Santa Cruz |
| **10** | San Juan | | **24** | Tierra del Fuego |
| **11** | San Luis | | | |
| **12** | Santa Fe | | | |
Los códigos **no son correlativos**: el `15` no se usa. La numeración es de ARCA y no sigue un orden alfabético ni geográfico, así que conviene tomarla de esta tabla en vez de deducirla.
Omitir el campo, o mandar **`null`**, significa "jurisdicción no informada" y es un valor válido.
## Por qué conviene mandarla
`stateId` es **opcional a nivel técnico pero muy recomendado**. Es el único dato **estructurado** de ubicación que queda guardado con el comprobante: `address` es texto libre y no se puede agrupar ni filtrar de forma confiable.
Con la provincia cargada podés después:
- Saber **cómo se reparte tu facturación por jurisdicción**, que es la base de cualquier liquidación de **Ingresos Brutos** y del **Convenio Multilateral**.
- Armar reportes de impuestos por provincia sin trabajo manual ni planillas paralelas.
- Hacer que el PDF imprima las **leyendas provinciales** que correspondan (defensa al consumidor, IIBB local). Sin `stateId`, esas leyendas no aparecen.
> **No se puede reconstruir hacia atrás**
>
> Si no mandaste la provincia al emitir, ese dato **no existe más**. No hay forma de deducirlo del domicilio en texto libre de manera confiable, y el comprobante ya está autorizado: no se modifica.
>
> Es una decisión que sólo se puede tomar una vez, en el momento de emitir.
## `null` es mejor que inventar
Mandá `null` cuando genuinamente no sepas de dónde es el receptor. Es un valor legítimo que significa "no informada".
**No pongas `0` por defecto**: `0` es CABA, y te ensucia los reportes con facturación que no es de CABA. Un `null` honesto es un dato que falta; un `0` inventado es un dato equivocado, y es peor porque no se nota.
> **Traelo del padrón**
>
> [`GET /:id-empresa/arca/padron/:docNumber`](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca) devuelve `stateId` ya codificado, y `null` cuando el contribuyente no tiene domicilio cargado. Podés reenviarlo tal cual.
## En JavaScript, PHP y Python
```js
PROVINCIAS.SANTA_FE // 12
PROVINCIAS.BUENOS_AIRES // 1
PROVINCIAS.CABA // 0
PROVINCIAS.CORDOBA // 3
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de tipo de comprobante de ARCA
> Tabla de códigos de tipo de comprobante de ARCA (AFIP) para facturación electrónica. Factura A, B y C, notas de crédito y débito, con el código que usa el campo voucherType.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/tipos-de-comprobante
Campo **`voucherType`** · en ARCA: **`CbteTipo`**
| Código | Comprobante | Letra |
| :---: | :--- | :---: |
| **1** | Factura A | A |
| **2** | Nota de Débito A | A |
| **3** | Nota de Crédito A | A |
| **6** | Factura B | B |
| **7** | Nota de Débito B | B |
| **8** | Nota de Crédito B | B |
| **11** | Factura C | C |
| **12** | Nota de Débito C | C |
| **13** | Nota de Crédito C | C |
## Cuál te corresponde
| Tu condición fiscal | Emitís |
| :--- | :--- |
| Responsable Monotributo | **C** (`11`, `12`, `13`) |
| IVA Sujeto Exento | **C** (`11`, `12`, `13`) |
| Responsable Inscripto → a otro Responsable Inscripto | **A** (`1`, `2`, `3`) |
| Responsable Inscripto → **a un Monotributista** | **A** (`1`, `2`, `3`) |
| Responsable Inscripto → a consumidor final o exento | **B** (`6`, `7`, `8`) |
Explicación completa: [Qué comprobante emitir](https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante).
> **Los comprobantes C no discriminan IVA**
>
> En `11`, `12` y `13` todas las líneas van al neto y no se informa IVA: los ítems no llevan `taxType`. Ver [cálculo de totales](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante#cálculo-de-totales).
## No emitidos por esta API
| Código | Comprobante |
| :---: | :--- |
| `51`, `52`, `53` | **Comprobantes M** |
| `4`, `9`, `15`, `54` | Recibos |
| `19`, `20`, `21` | Comprobantes de exportación (E) |
| `91` | Remito |
Existen en el sistema pero no se emiten por API. Cualquier otro código responde `VALIDATION_ERROR`.
## En JavaScript, PHP y Python
```js
TIPO_COMPROBANTE.FACTURA_C // 11
TIPO_COMPROBANTE.NOTA_CREDITO_C // 13
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de tipo de documento de ARCA
> Tabla de códigos de tipo de documento de ARCA (AFIP) para facturación electrónica: CUIT 80, CUIL 86, DNI 96, pasaporte y consumidor final sin identificar.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/tipos-de-documento
Campo **`customer.docType`** · en ARCA: **`DocTipo`**
| Código | Tipo de documento | Formato de `docNumber` |
| :---: | :--- | :--- |
| **80** | **CUIT** | 11 dígitos, sin guiones |
| **86** | CUIL | 11 dígitos, sin guiones |
| **87** | CDI | 11 dígitos |
| **96** | **DNI** | 7 u 8 dígitos |
| **94** | Pasaporte | Alfanumérico |
| **91** | CI Extranjera | Alfanumérico |
| **99** | **Sin identificar** (consumidor final) | `"0"` |
`docNumber` va siempre **sin puntos ni guiones**: `"30703088534"`, no `"20-11222333-4"`.
## Cuál usar
| Situación | `docType` | `docNumber` |
| :--- | :---: | :--- |
| Empresa o responsable inscripto | `80` CUIT | Su CUIT |
| Monotributista | `80` CUIT | Su CUIT |
| Persona con CUIT | `80` CUIT | Su CUIT |
| Persona con DNI | `96` DNI | Su DNI |
| Consumidor final identificado | `96` DNI | Su DNI |
| Consumidor final no identificado | `99` | `"0"` |
| Extranjero | `94` Pasaporte | Su pasaporte |
> **Factura A exige CUIT**
>
> En los comprobantes **A** (`1`, `2`, `3`), `docType` tiene que ser **`80`** y el receptor tiene que ser Responsable Inscripto o Monotributista. Si tu cliente es consumidor final o exento, el comprobante que corresponde es **B**.
> **Consumidor final sin identificar: sólo hasta cierto importe**
>
> `docType: 99` con `docNumber: "0"` se admite en **Factura B y C** únicamente por debajo del importe que fija ARCA. Por encima hay que identificar al comprador con DNI o CUIT.
>
> ARCA actualiza ese umbral periódicamente: no lo hardcodees. Si te pasás, el rechazo llega con el código **`10015`** y un mensaje explícito. Ver [errores](https://asistentefacturaelectronica.com/ayuda/api/errores#422-arca_error--arca-rechazó-el-comprobante).
>
> **Pedí siempre el documento si lo tenés.** Además de evitar el rechazo, hace que el comprobante sea buscable después por cliente.
## Verificar un documento
No es obligatorio, pero podés consultar el padrón de ARCA con el CUIT o CUIL del cliente:
```http
GET /:id-empresa/arca/padron/30703088534
```
Devuelve `docType` y `docNumber` ya normalizados, junto con el nombre y la condición fiscal. Ver [Consultas a ARCA](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca).
## En JavaScript, PHP y Python
```js
TIPO_DOCUMENTO.CUIT // 80
TIPO_DOCUMENTO.DNI // 96
TIPO_DOCUMENTO.SIN_IDENTIFICAR // 99
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de tributo de ARCA
> Tabla de códigos de tributo de ARCA (AFIP) para facturación electrónica: Ingresos Brutos, percepciones de IVA e IIBB, tasas municipales e impuestos internos.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/tributos
Campo **`tributes[].type`** · en ARCA: **`Tributos[].Id`**
Son los impuestos y percepciones que **no son IVA**. Se informan aparte y suman al total del comprobante.
| Código | Tributo |
| :---: | :--- |
| **1** | Impuestos nacionales |
| **2** | Impuestos provinciales |
| **3** | Tributos municipales |
| **4** | Impuestos internos |
| **5** | **Ingresos Brutos (IIBB)** |
| **6** | Percepción de IVA |
| **7** | **Percepción de IIBB** |
| **8** | Percepciones por tributos municipales |
| **9** | Otras percepciones |
| **10** | Impuesto interno a nivel ítem |
| **13** | Percepción de IVA a no categorizado |
| **14** | Retención IIGG – RG 830 |
| **15** | Retención IVA – RG 3873 |
| **16** | Pago a cuenta IVA – RG 3873 |
| **17** | Percepción IVA RG 3873 |
| **18** | Retención IVA – RG 2616/2009 |
| **19** | Retención Ganancias – RG 2616/2009 |
| **99** | Otros |
## Cómo se informan
A diferencia de los ítems, acá **el importe lo calculás vos**: la API manda a ARCA el `subtotal` tal como lo enviás.
```json
{
"tributes": [
{
"type": 7,
"description": "Percepción IIBB CABA",
"baseAmount": 100000,
"aliquot": 3.5,
"subtotal": 3500
}
]
}
```
| Campo | Obligatorio | En ARCA | Descripción |
| :--- | :---: | :--- | :--- |
| `type` | **Sí** | `Id` | Código de esta tabla |
| `baseAmount` | **Sí** | `BaseImp` | Base imponible |
| `aliquot` | **Sí** | `Alic` | Alícuota aplicada |
| `subtotal` | **Sí** | `Importe` | Importe determinado |
| `description` | No | `Desc` | Descripción que se imprime |
La suma de todos los `subtotal` va a `ImpTrib` y se incluye en el total del comprobante.
> **La mayoría de las integraciones no usa este campo**
>
> `tributes` es opcional y sólo corresponde si sos agente de percepción o retención, o si tenés que informar impuestos internos o tasas municipales. Si no es tu caso, omitilo.
## Transparencia fiscal al consumidor
Es calculado por la API en base a lo que informas en tributos, para alinearse con en el **Régimen de Transparencia Fiscal al Consumidor** (Ley 27.743).
## En JavaScript, PHP y Python
```js
TRIBUTO.IIBB // 5
TRIBUTO.PERCEPCION_IIBB // 7
TRIBUTO.PERCEPCION_IVA // 6
TRIBUTO.IMPUESTOS_INTERNOS // 4
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Códigos de unidad de medida de ARCA
> Tabla de códigos de unidad de medida de ARCA (AFIP) para facturación electrónica: unidades, kilogramos, metros, litros, horas y el resto, para el campo unitType.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tablas/unidades-de-medida
Campo **`items[].unitType`** · por defecto **`7`** (unidades)
Es opcional: si no lo mandás, se usa `7`. Sólo importa si vendés por peso, volumen o medida.
| Código | Unidad | | Código | Unidad |
| :---: | :--- | :--- | :---: | :--- |
| **7** | **Unidades** *(por defecto)* | | **14** | Gramos |
| **1** | Kilogramos | | **17** | Kilómetros |
| **2** | Metros | | **20** | Centímetros |
| **3** | Metros cuadrados | | **29** | Toneladas |
| **4** | Metros cúbicos | | **47** | Mililitros |
| **5** | Litros | | **96** | Packs |
| **8** | Pares | | **98** | Otras unidades |
| **9** | Docenas | | | |
ARCA define muchas más (quilates, hectolitros, curie, kilogramo base, etc.). Se admite el catálogo completo; un código no reconocido se imprime como "unidades".
> **Servicios por hora**
>
> No hay un código de "hora" en la tabla de ARCA. Lo habitual es usar `7` (unidades) con la cantidad de horas y la descripción aclarándolo: *"Consultoría — 8 horas"*.
## Cantidades fraccionadas
Si vendés por peso o volumen vas a necesitar decimales. Por defecto la cantidad se redondea a **2 decimales**; subilo con `settings`:
```json
{
"settings": { "quantityPrecision": 4, "pricePrecision": 4 },
"items": [
{ "description": "Café en grano", "quantity": 0.7525, "unitType": 1, "price": 12500, "taxType": 5 }
]
}
```
`quantityPrecision` y `pricePrecision` aceptan `2`, `4` o `6`. Los importes resultantes se redondean siempre a 2 decimales, que es lo que exige ARCA. Ver [precisión](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante#precisión-settings).
## En JavaScript, PHP y Python
```js
UNIDAD.UNIDADES // 7
UNIDAD.KILOGRAMOS // 1
UNIDAD.LITROS // 5
UNIDAD.METROS // 2
```
Constantes para los tres lenguajes en el [repositorio de ejemplos](https://asistentefacturaelectronica.com/ayuda/api/ejemplos).
---
# Qué comprobante emitir según tu condición fiscal
> Factura A, B o C según seas monotributista o responsable inscripto y según quién sea tu cliente. Notas de crédito y débito, y cómo anular un comprobante electrónico de ARCA.
Fuente: https://asistentefacturaelectronica.com/ayuda/api/tipos-de-comprobante
La letra del comprobante sale de **dos** datos: tu condición fiscal como emisor y la de tu cliente. Elegir mal es la causa más frecuente de rechazo de ARCA.
## Según tu condición fiscal
### Sos Monotributista o Exento → **siempre C**
| Comprobante | `voucherType` |
| :--- | :---: |
| Factura C | `11` |
| Nota de Débito C | `12` |
| Nota de Crédito C | `13` |
A **cualquier** cliente: consumidor final, responsable inscripto, otro monotributista. La letra no cambia.
**No discriminás IVA.** Todas las líneas van al neto y los ítems no llevan `taxType`. Es correcto: el monotributo no discrimina IVA. Ver [cálculo de totales](https://asistentefacturaelectronica.com/ayuda/api/emitir-comprobante#cálculo-de-totales).
### Sos Responsable Inscripto → depende del cliente
| Tu cliente es… | Emitís | `voucherType` (Factura / N.D. / N.C.) |
| :--- | :--- | :--- |
| **Responsable Inscripto** (`taxType: 1`) | **A** | `1` / `2` / `3` |
| **Responsable Monotributo** (`6`) | **A** | `1` / `2` / `3` |
| Consumidor final (`5`) | **B** | `6` / `7` / `8` |
| IVA Sujeto Exento (`4`) | **B** | `6` / `7` / `8` |
En **A** el receptor tiene que estar identificado con **CUIT** (`docType: 80`). En **B** se calcula el IVA pero no se imprime discriminado.
> **Al monotributista se le emite Factura A, no B**
>
> Es el error más frecuente al integrar, porque durante años fue al revés. Un responsable inscripto que le vende a un monotributista emite **Factura A** con el IVA discriminado.
> **¿No sabés la condición de tu cliente?**
>
> Consultala en el padrón de ARCA con [`GET /:id-empresa/arca/padron/:cuit`](https://asistentefacturaelectronica.com/ayuda/api/consultas-arca). Devuelve `taxCondition`, que podés usar tal cual como `customer.taxType`.
## Notas de crédito y débito
Un comprobante electrónico autorizado **no se puede modificar ni borrar**. Para corregirlo emitís otro que lo compensa:
| Querés… | Emitís | Monotributista o exento | Responsable Inscripto |
| :--- | :--- | :---: | :---: |
| Anular una factura entera | **Nota de crédito** por el total | `13` | `3` (sobre A) · `8` (sobre B) |
| Descontar una parte (devolución, bonificación) | **Nota de crédito** por esa parte | `13` | `3` (sobre A) · `8` (sobre B) |
| Cobrar de más (intereses, gastos, ajuste) | **Nota de débito** | `12` | `2` (sobre A) · `7` (sobre B) |
El código depende de la letra de la **factura original**, no de tu condición: si emitiste una Factura A, la anulás con una Nota de Crédito A.
Dos reglas que no se negocian:
1. **Misma letra que el comprobante original.** Una nota de crédito C anula una factura C. Nunca cruzadas.
2. **Siempre con `associatedVouchers`**, apuntando al comprobante que modifica.
```json title="Anular la Factura C 0001-00000045"
{
"idempotency": "a4e1f2c3-…",
"posNumber": 1,
"voucherType": 13,
"concept": 1,
"voucherDate": "20260814",
"associatedVouchers": [
{ "voucherType": 11, "posNumber": 1, "voucherNumber": 45 }
],
"customer": { "docType": 96, "docNumber": "35888999", "name": "Pedro López", "taxType": 5 },
"items": [
{ "description": "Anulación de Factura C 0001-00000045", "quantity": 1, "price": 30000, "taxType": 3 }
]
}
```
Los ítems de la nota de crédito deben reflejar **lo que se anula**: por el total si anulás todo, o sólo las líneas devueltas si es parcial. La API calcula los importes igual que en una factura.
> **No existe "anular" como operación**
>
> No hay ningún endpoint que borre o anule un comprobante. Emitir la nota de crédito **es** la anulación, y así lo entiende ARCA. Los dos comprobantes quedan en tu historial: eso es lo correcto desde el punto de vista fiscal.
## Cómo se numeran
Cada combinación de **punto de venta + tipo de comprobante** lleva su propia numeración correlativa, y la API la administra sola:
```text
Punto de venta 1, Factura C → 0001-00000045, 0001-00000046, …
Punto de venta 1, N. Crédito C → 0001-00000012, 0001-00000013, …
Punto de venta 2, Factura C → 0002-00000008, 0002-00000009, …
```
Son secuencias independientes. **No lleves contadores en tu sistema**: mandá el `posNumber` y el `voucherType`, y el `voucherNumber` viene en la respuesta.
## Lo que esta API no emite
| Comprobante | Códigos | |
| :--- | :--- | :--- |
| **Comprobantes M** | `51`, `52`, `53` | No se emiten por API |
| Recibos | `4`, `9`, `15`, `54` | Existen en el sistema, no se emiten por API |
| Comprobantes de exportación (E) | `19`, `20`, `21` | No se emiten por API |
| Remito | `91` | No se emite por API |
Cualquier otro código responde `VALIDATION_ERROR`. Si necesitás alguno de estos, escribinos a [contacto@nanofactura.com](mailto:contacto@nanofactura.com).
La tabla completa de códigos: **[Tipos de comprobante](https://asistentefacturaelectronica.com/ayuda/api/tablas/tipos-de-comprobante)**.