Saltar al contenido principal

Emitir un comprobante

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.

Campos principales

CampoObligatorioTipoEn ARCADescripción
idempotencyStringClave única de este comprobante. Máx. 128 caracteres: letras, números, - y _. Ver idempotencia.
posNumberIntegerPtoVtaPunto de venta. Tiene que estar dado de alta en NanoFactura y habilitado en ARCA.
voucherTypeIntegerCbteTipoTipo de comprobante (tabla).
conceptIntegerConcepto1 productos · 2 servicios · 3 productos y servicios.
voucherDateStringCbteFchFecha del comprobante, YYYYMMDD.
customerObjectDatos del receptor (ver tabla).
itemsArrayLíneas de detalle, mínimo 1 (ver tabla).
serviceFromCondicionalStringFchServDesdeInicio del servicio, YYYYMMDD. Obligatorio si concept es 2 o 3.
serviceToCondicionalStringFchServHastaFin del servicio, YYYYMMDD. Obligatorio si concept es 2 o 3.
serviceExpirationCondicionalStringFchVtoPagoVencimiento del pago, YYYYMMDD. Obligatorio si concept es 2 o 3.
associatedVouchersCondicionalArrayCbtesAsocComprobante que se modifica. Necesario en notas de crédito y débito (ver tabla).
currencyNoObjectMoneda. Por defecto, pesos argentinos (ver tabla).
tributesNoArrayTributosImpuestos y percepciones que no son IVA (ver tabla).
emailsNoArrayDestinatarios del envío automático del PDF (ver tabla).
settingsNoObjectPrecisión decimal de cantidades y precios (ver tabla).
legendNoStringLeyenda impresa en el comprobante. Máx. 200 caracteres.
commentsNoStringComentario interno del emisor, no se imprime. Máx. 512 caracteres.
externalReferenceNoStringIdentificador de tu sistema. Máx. 128 caracteres. Filtrable en el listado.
additionalDataNoObjectObjeto clave-valor libre que se guarda junto al comprobante.
cancelsInSameForeignCurrencyNoStringCanMisMonExt'S' o 'N'. Sólo aplica en moneda extranjera.
receiverTaxConditionIdNoIntegerCondicionIVAReceptorIdSi 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.

Cliente (customer)

CampoObligatorioTipoEn ARCADescripción
docTypeIntegerDocTipoTipo de documento (tabla).
docNumberStringDocNroNúmero sin guiones ni puntos.
nameStringNombre, apellido o razón social.
taxTypeIntegerCondicionIVAReceptorIdCondición frente al IVA (tabla).
addressNoStringDomicilio. Se imprime en el comprobante.
stateIdRecomendadoInteger | nullProvincia (tabla).
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.

Ítems (items)

Mínimo uno. Cada objeto es una línea de detalle.

CampoObligatorioTipoDescripción
descriptionStringDescripción del producto o servicio.
quantityNumberCantidad. Mínimo 0.0001.
priceNumberPrecio unitario, sin descuento. Mínimo 0.
taxType, salvo en CIntegerAlícuota de IVA de la línea (tabla). Los comprobantes C no la llevan: no discriminan IVA.
codeNoStringTu código interno del producto.
unitTypeNoIntegerUnidad de medida (tabla). Por defecto 7 (unidades).
discountNoNumberDescuento de la línea. Por defecto 0.
discountTypeNoEnum'percentage' (defecto) o 'amount'.

Moneda (currency)

CampoObligatorioTipoEn ARCADescripción
idNoStringMonIdCódigo de moneda (tabla). Por defecto 'PES'.
quotationNoNumberMonCotizCotizació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.

CampoObligatorioTipoEn ARCADescripción
typeIntegerIdTipo de tributo (tabla).
baseAmountNumberBaseImpBase imponible.
aliquotNumberAlicAlícuota aplicada.
subtotalNumberImporteImporte del tributo.
descriptionNoStringDescDescripció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.

CampoObligatorioTipoEn ARCADescripción
voucherTypeIntegerTipoTipo del comprobante asociado.
posNumberIntegerPtoVtaPunto de venta del asociado.
voucherNumberIntegerNroNúmero del asociado.
cuitNoStringCuitCUIT del emisor, sólo si es distinto al tuyo.
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.

Correos (emails)

Despacha el PDF por correo en segundo plano, sin bloquear la respuesta.

CampoObligatorioTipoDescripción
emailStringDirección del destinatario.
nameNoStringNombre del destinatario.
commentsNoStringTexto a incluir en el cuerpo del correo.
pdfVersionsNoArrayCopias 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)

CampoTipoDescripción
quantityPrecisionIntegerDecimales de la cantidad: 2 (defecto), 4 o 6.
pricePrecisionIntegerDecimales 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:

subtotal = quantity × price
descuento = discountType === 'percentage' ? subtotal × discount / 100 : discount
neto = subtotal − descuento

El destino del neto depende del taxType de la línea:

taxTypeTratamientoVa a¿Genera IVA?
4, 5, 6, 8, 9GravadoImpNeto
2, 3Exento / 0%ImpOpExNo
1 u otroNo gravadoImpTotConcNo

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 ARCADe dónde sale
CbteDesde / CbteHastaNúmero correlativo que reserva la API.
ImpNetoSuma de las líneas gravadas (y de todas en comprobantes C).
ImpIVASuma del IVA calculado por línea.
ImpOpExSuma de las líneas exentas o al 0%.
ImpTotConcSuma de las líneas no gravadas.
ImpTribSuma de los tributes[].subtotal.
ImpTotalLa 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.

{ "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.

{
"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:

{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)

{
"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 }
}
}
CampoDescripción
idIdentificador interno. Es el que se usa en las rutas de PDF y email.
voucherNumberNúmero correlativo dentro del punto de venta y tipo.
caeCAE otorgado por ARCA.
totalAmountImporte total calculado.
arcaObservationsnull, o un array [{ code, msg }] con observaciones que ARCA devolvió aprobando igual el comprobante. Conviene registrarlas.
idempotencyHittrue 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.

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.

CampoDescripción
grantedCupo total del período (los adicionales contratados ya están sumados).
usedConsumido, incluyendo este comprobante.
remaininggranted − used, con piso en 0. Nunca es negativo.
periodEndDía en que se renueva el cupo.
breakdownConsumo 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.

Errores

Los cuatro que vas a ver en la práctica:

HTTPcodeQué pasó¿Reintentar?
400VALIDATION_ERRORPayload inválido o punto de venta inexistenteNo, corregí el pedido
422ARCA_ERRORARCA rechazó el comprobanteNo, corregí el pedido
429VOUCHER_LOCK_TIMEOUTOtra emisión del mismo punto de venta sigue en curso, con la misma idempotency
504Se agotó el tiempo del pedido, 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.

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.

Ejemplo completo

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": "[email protected]", "name": "Pedro López", "pdfVersions": [1] }
],
"externalReference": "EXT-ORD-99882",
"legend": "Gracias por su compra"
}

Implementaciones completas en JavaScript, PHP y Python: Ejemplos.