# 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)**.

