Guías6 de 11

Cómo se numera una serie

La numeración de comprobantes es correlativa y sin huecos, por punto de venta y por tipo. No es una convención administrativa: ARCA rechaza el comprobante N+1 si el N nunca existió, y una serie trabada no se destraba reintentando.

Esta guía es sobre lo que Emissa hace por vos y sobre la única cosa que tenés que hacer bien: mandar Idempotency-Key.

Una serie por punto de venta y por tipo

Cada combinación de punto de venta y tipo de comprobante lleva su propio contador independiente. Está desarrollado en qué es un punto de venta: tres emisiones desde el mismo punto de venta 0001 quedaron en 0001-00000131 (Factura B), 0001-00000033 (nota de crédito B) y 0001-00000003 (Factura A).

Por qué un hueco no es un detalle

Una serie fiscal es una sucesión, y ARCA la valida como tal. Si tu sistema pide el número 7 cuando ARCA registró hasta el 5, el organismo rechaza con el error 10016 —el número no es el que corresponde— y el rechazo no arregla nada: al siguiente intento el desfasaje sigue igual, o peor.

Ese modo de falla se midió en este proyecto: contando los comprobantes rechazados como si ocuparan número, cada intento empeoraba el desfasaje en uno y la serie de homologación se fue de 4 a 521 sin volver a emitir. Es la razón por la que las reglas de abajo existen y no son celo de más.

Quién decide el número

ARCA, no Emissa. Antes de cada emisión se consulta FECompUltimoAutorizado, que es el último número que el organismo tiene autorizado en esa serie, y se sigue desde ahí.

Es deliberado y tiene una consecuencia útil: si alguien emitió desde otro sistema con el mismo punto de venta, ARCA está adelante, y seguir por la cuenta local produciría un rechazo. Preguntándole al organismo, la serie converge sola.

Adentro se toma el mayor entre el número de ARCA y el último local, así que ninguna de las dos fuentes puede hacerte retroceder.

Dos pedidos al mismo tiempo

Ésta es la parte que un integrador necesita saber para no escribir un lock propio: no hace falta. Emissa serializa la asignación del número.

Tres emisiones lanzadas en paralelo contra el mismo contribuyente y la misma serie, con tres Idempotency-Key distintas:

concurrente-1 [HTTP 201] [0,22 s]   0001-00000133   cae 86300697123075
concurrente-2 [HTTP 201] [2,21 s]   0001-00000134   cae 86300697123135
concurrente-3 [HTTP 201] [2,49 s]   0001-00000135   cae 86300697123148

Tres números consecutivos, tres CAE distintos, ninguna colisión. Y contando en la base después:

number | status     | cae
   132 | AUTHORIZED | 86300697121691
   133 | AUTHORIZED | 86300697123075
   134 | AUTHORIZED | 86300697123135
   135 | AUTHORIZED | 86300697123148

números repetidos: 0 filas
CAE repetidos:     0 filas

Los tiempos cuentan la historia: el primero entró y salió en 0,22 s, los otros dos esperaron su turno. Eso es el lock funcionando, y es lo que garantiza que la serie no tenga ni saltos ni repetidos.

Lo que se serializa es más chico de lo que parece

Sólo resolver el número y escribir la fila ocurre dentro del lock. La consulta a ARCA y el pedido de CAE quedan afuera:

[ afuera ]   FECompUltimoAutorizado
[ ADENTRO ]  lock → último local → número → INSERT → commit
[ afuera ]   FECAESolicitar y guardado del CAE

Por qué importa para vos: si el pedido de CAE —que puede tardar segundos— estuviera adentro, dos clientes del mismo contribuyente emitiendo a la vez se esperarían uno al otro todo ese tiempo. Con la sección crítica acotada, el segundo espera lo que tarda un INSERT.

El estado del comprobante decide si su número se libera

Acá hay una asimetría que conviene entender, porque explica qué pasa después de un fallo:

Estado¿Ocupa número?Por qué
AUTHORIZEDARCA lo registró. Es definitivo
REJECTEDNoARCA nunca lo registró: del otro lado ese número sigue libre
PENDINGNo se sabe si ARCA lo registró
UNKNOWNNo se sabe si ARCA lo registró

Un REJECTED no cuenta y su fila se borra para liberar el número. Si contara, sería exactamente el caso del 4 al 521 de más arriba.

PENDING y UNKNOWN sí cuentan, y es la regla más importante de esta guía. De esos no se sabe si el organismo los registró, así que reutilizar su número podría duplicar un comprobante fiscal. Se resuelven preguntándole a ARCA con POST /v1/invoices/{id}/reconcile; si la respuesta es que no existe, pasan a REJECTED y recién entonces su número vuelve a estar disponible.

Los que quedaron en ese limbo se listan con GET /v1/invoices/unresolved:

curl "https://api.emissa.com.ar/v1/invoices/unresolved?taxpayerId=TU_TAXPAYER_ID" \
  -H "X-API-Key: $EMISSA_API_KEY"
[]

Vacío es lo que querés ver. Qué hacer cuando no lo está está en qué hacer cuando el CAE viene rechazado.

Idempotency-Key es obligatorio, y por qué

Es el único requisito que la numeración te impone. Sin el header, el pedido no entra:

{
  "statusCode": 400,
  "errorCode": "IDEMPOTENCY_KEY_REQUIRED",
  "message": "Falta el header Idempotency-Key. Es obligatorio en las operaciones que emiten un comprobante."
}

No es burocracia y no es opcional por una razón concreta: un comprobante fiscal duplicado no se deshace. No hay DELETE. Lo único que se puede hacer es emitir una nota de crédito que lo anule, y las dos cosas —la factura de más y su nota— quedan en tu historia y en la de ARCA para siempre.

Con la clave, repetir el mismo pedido devuelve el mismo comprobante:

HTTP 201 · Idempotent-Replay: true · 0,02 segundos
id devuelto: cms6l4gnq0007ur04xl1dcqf0 | cae: 86300696468093 | number: 131

Mismo id, mismo CAE, mismo número, y no se emitió un segundo: verificado contando en la base, siguió habiendo uno con ese CAE.

Cómo elegir la clave

La clave tiene que identificar la intención de facturar, no el intento. La regla práctica: derivala de algo que ya sea único en tu dominio y que no cambie al reintentar.

  • Bien: el id de la suscripción más el período — sub_8123-2026-07. Si tu proceso corre dos veces, la clave es la misma y el segundo pedido no emite nada.
  • Mal: un UUID nuevo por intento. Es lo mismo que no mandar nada: cada reintento se ve como una factura distinta.
  • Mal: el timestamp. Mismo problema.

Un reintento con una clave distinta es una emisión nueva, y es correcto que lo sea: la API no puede saber que quisiste decir lo mismo.

Qué sigue