Antes de escribir código: el reparto
Hay una división que conviene entender primero porque determina la arquitectura de tu integración, y no se deduce leyendo la lista de endpoints.Qué se hace sólo en el panel y qué con una API key
cuitAAutorizar cuál es la CUIT a autorizar.
Qué contesta la API si la key no alcanza
Qué contesta la API si la key no alcanza
taxpayer:write, el alta devuelve el scope que falta:API_KEY_INSUFFICIENT_PERMISSIONS. Y lo que sí es del panel —registrar un webhook,
por ejemplo— devuelve 403 API_KEY_NOT_ALLOWED_HERE, con el mensaje «Esta operación
no se puede hacer con API key: requiere una sesión de usuario».1 · El contribuyente
Se da de alta desde el panel o conPOST /v1/taxpayers y una key con taxpayer:write. Los datos son los que van impresos en el
comprobante, así que salen de la constancia de inscripción del CUIT, no de lo que
uno recuerda.
Las tres decisiones de este paso:
Los campos del alta
Los campos del alta
2 · El certificado
Desde el panel o conPOST /v1/arca/credentials y una key con credential:write: el .crt que te dio ARCA y su clave privada. Es opcional si el contribuyente autorizó a Emissa en ARCA. Emissa los
guarda cifrados y los usa para firmar cada pedido; la clave privada no vuelve a
salir.
Al consultar las credenciales, la API devuelve sólo metadatos —sujeto, emisor,
número de serie, huella, vigencia y estado—. Y revocar una credencial no la
borra: el rastro de qué certificado estuvo activo y hasta cuándo es lo que
permite reconstruir con qué se firmó cada comprobante.
Qué contesta si emitís sin haber cargado la credencial
Qué contesta si emitís sin haber cargado la credencial
3 · La API key
Acá se toman las dos decisiones que más se arrepienten después.El modo
test opera contra homologación, live contra producción.
Los scopes
Pedí sólo los que tu proceso usa.Si falta un scope, la respuesta dice cuál
Si falta un scope, la respuesta dice cuál
invoice:read solamente, intentando anular. La
misma clave intentando emitir devuelve La API key no declara invoice:write.De dónde sale el id del contribuyente
Todas las operaciones de emisión lo piden, y no es el CUIT. Con la clave ya creada,GET /v1/taxpayers lista tus contribuyentes con su id (alcanza con
taxpayer:read), y ?cuit= encuentra el de un CUIT puntual, con o sin guiones. Si
justo lo estás dando de alta y ya existía, el 409 del alta trae el id: mirá
la sección «Varios productos en una organización», más abajo.
Identificá tu producto
Si tu cuenta de Emissa sirve a más de un producto —o querés separar el consumo de cada integración—, mandá en cada pedido la cabeceraX-Emissa-Producto con el
nombre de tu producto:
- Es opcional y sólo vale con API key. Sin ella, o vacía, el producto queda en
null. - Formato: de 1 a 32 caracteres entre minúsculas, números y guiones
(
^[a-z0-9-]{1,32}$). Cualquier otra cosa devuelve400 INVALID_INPUT, con el formato esperado en el mensaje. - Se estampa al emitir y no cambia. Un reintento con la misma
Idempotency-Keydevuelve el comprobante original aunque mandes otra etiqueta. - Dónde la leés después: el comprobante trae
producto;GET /v1/invoices/search?producto=inmosfiltra;GET /v1/consumosumaporProducto([{ producto, total, billable }]) y acepta?producto=; el eventoinvoice.resolvedtraeproductoendata; y el registro de pedidos del panel se puede filtrar por producto.
Varios productos en una organización
Si más de un producto tuyo (inmos, define…) opera dentro de una sola
organización de Emissa, la cabecera sola no alcanza: cualquier key llega a todos
los contribuyentes de la organización. Para que el de un producto no vea ni opere
los de otro, atá la key al producto.
1. Atá la key. En el panel, al crear la key, completá Producto
(inmos). Una key que ya existía se puede atar una sola vez, desde la misma
pantalla; después no se cambia. Si te equivocás, creás otra.
2. Qué cambia para esa key:
- Cada pedido queda etiquetado con ese producto, mandes o no
X-Emissa-Producto. Si mandás otro valor, la respuesta es403 API_KEY_PRODUCTO_DISTINTOy no se hace nada. Antes se mira el formato: una cabecera mal formada (INMOS!, con mayúsculas, con espacios, de más de 32 caracteres) es400 INVALID_INPUT, no403. Un valor que no es un producto no es «otro producto»: el403mandaría a revisar la key cuando lo que está mal es la cabecera. Gana el formato; la comparación con la key viene después. - Sólo ve y opera los contribuyentes que incluyen su producto: emitir, anular,
reconciliar, buscar, exportar, el PDF, verificar la conexión, las credenciales,
el logo y el consumo. Un contribuyente sin tu producto entre los suyos, o
sin ninguno, responde
403 INSUFFICIENT_TAXPAYER_PERMISSIONS: igual que uno de otra organización, a propósito. Decir «es de otro producto» confirmaría que ese id existe. - Se combina con la restricción por contribuyentes: si además la key está acotada a una lista, la lista sigue valiendo.
inmos y define, el contribuyente tiene productos: ["inmos", "define"]
y las keys de los dos lo operan. Una key de un tercer producto no. Los productos
de un contribuyente:
- Los crea el alta:
POST /v1/taxpayersde un CUIT nuevo lo crea conproductos = [el producto de tu key]o, si la key no está atada,[el de X-Emissa-Producto]. - No cambian por API. Sumar o quitar productos lo hace el dueño de la cuenta desde el panel, con auditoría de la lista de antes y la de después. Es también la forma de migrar los contribuyentes que ya existían, que quedaron sin producto.
409 y, si el contribuyente
incluye tu producto entre los suyos, trae su id en details.taxpayerId —también
en producción—:
409 viene sin id: el CUIT está tomado, pero no es un
contribuyente que tu key alcance. Y no te suma a él: una key no se apropia de un
contribuyente ajeno. Para que tu producto lo opere, pedile al dueño de la cuenta que
lo sume desde el panel.
5. Listar los tuyos. GET /v1/taxpayers devuelve los contribuyentes que tu key
ve, paginados como el resto de las listas (pagination.nextCursor), con
?producto=, ?cuit= y ?environment=. Cada fila es la misma que devuelve el alta,
más productos. ?producto= filtra por «contiene»: un contribuyente de inmos y
define aparece bajo los dos. Una key atada a un producto sólo ve los suyos, aunque
pidas otro ?producto=.
producto (singular) está deprecado. La API lo sigue devolviendo un
tiempo, con el primer valor de productos, para el cliente que ya lo leía.
Con más de un producto no dice cuáles son los demás: leé productos.producto en el comprobante), no con el del
contribuyente. Dos cosas a tener presentes:
- El acceso es por contribuyente, no por comprobante. Si el mismo contribuyente es
de
inmosy dedefine, la key de cada uno puede buscar, ver, anular y exportar todos sus comprobantes, también los que emitió el otro producto. Es lo que significa compartir el contribuyente: comparten también la numeración de la serie. Para filtrar lo tuyo, usá?producto=en la búsqueda. - El consumo sí se separa.
GET /v1/consumode una key atada devuelve sólo el consumo de su producto, también sobre un contribuyente compartido; un?producto=de otro devuelve vacío. La cuota de la organización (organization) sigue siendo una sola.
credential.expiring,
connection.established) llega a los endpoints de todos sus productos; y lo de
la organización (quota.exceeded, api_key.expiring) llega a todos. Un endpoint
sin producto es general y recibe todo, como siempre. El detalle, en
los webhooks.
8. La Idempotency-Key es única por organización, no por producto. Dos
productos que comparten organización comparten también el espacio de claves: si
inmos y define mandan la misma clave, el segundo recibe
409 IDEMPOTENCY_KEY_REUSED aunque sean pedidos distintos. No se filtra ningún
dato, pero el pedido no se hace. Cada producto tiene que usar un UUID por
intento o prefijar la clave con su producto —inmos-sub_8841-2026-10, no
sub_8841-2026-10—. Si derivás la clave de tu dominio, el prefijo es lo que la
mantiene distinta de la de tu vecino.
4 · Emitir
Si ARCA rechaza el comprobante recibís un422 ARCA_WSFEV1_REJECTED —un rechazo
fiscal, no una falla de Emissa— y el motivo viaja en details.arca, también en
producción: { errores: [{ codigo, mensaje }], observaciones: [{ codigo, mensaje }] }.
Es el texto literal de ARCA (recortado a 500 caracteres y 20 ítems), pensado para que tu
cliente corrija el dato —el receptor, la condición de IVA, la fecha— y reintente. El mismo
motivo queda en el comprobante (rechazoArca) y en el webhook invoice.resolved. El detalle
está en los errores de la API.
Lo que no tenés que hacer: llevar tu propia numeración, ni tomar un lock, ni
reservar números. La serie la resuelve Emissa contra ARCA, y está medido que tres
emisiones simultáneas salen consecutivas.
5 · Consultar
Tres formas, para tres propósitos distintos:Cómo se pagina con cursor
Cómo se pagina con cursor
data, con los comprobantes
completos, y pagination. Ésta es la pagination textual de una corrida con
limit=2:cursor. Verificado: la primera página trajo los
comprobantes 135 y 134, y la segunda el 133 y el 132. El orden es descendente por
fecha de creación.Un cursor no se desordena cuando entran comprobantes nuevos mientras recorrés,
que es lo que sí le pasa a un offset.Cómo se exporta
Cómo se exporta
6 · Anular
Un comprobante autorizado no se borra: se anula con una nota de crédito que lo referencia, y Emissa la emite por vos eligiendo el tipo correcto. La nota tiene su propio CAE —es un comprobante fiscal por derecho propio— y la factura original queda anulada conservando el suyo.7 · El PDF
Se genera al pedirlo, así que no hay nada que almacenar de tu lado. Trae el bloque del emisor, el detalle, las leyendas de transparencia fiscal y el QR de la RG 4892.8 · Lo que puede salir mal y no es culpa tuya
Un pedido a ARCA puede quedarse sin respuesta. Cuando eso pasa, el comprobante no queda ni autorizado ni rechazado: queda en un estado que hay que resolver preguntándole al organismo.El orden, de principio a fin
Dar de alta el contribuyente, en el panel
Cargar el certificado, en el panel
Crear la API key
Guardar el id de tu contribuyente
Emitir con una clave de idempotencia derivada de tu dominio
Guardar el id que devuelve la emisión
Loguear el request id y el modo de cada respuesta
Consultar por id, paginar con cursor, exportar con NDJSON
Anular con una clave de idempotencia propia
Revisar los comprobantes sin resolver de forma periódica