facturaUno Docs de integradores — API v1

Emitir comprobantes por API

Una sola API para emitir facturas, notas de crédito y notas de débito electrónicas en Argentina (ARCA) y Paraguay (SIFEN / e-Kuatia). La emisión es siempre asíncrona: la API responde al instante y el comprobante se autoriza en segundos; te enterás por webhook firmado, por polling o por el link público.

1. Autenticación

Todas las llamadas a /api/v1/* llevan el header X-Api-Key. Las keys se emiten por integración y tienen una o dos audiencias:

AudienciaPermite
facturauno.issueEmitir, cancelar, suscribir webhooks — y también todo lo de lectura.
facturauno.readSolo consultar (detalle, listado, XML, catálogo).

Auth inválida o ausente responde 401/403 con cuerpo genérico. Los errores de negocio tienen siempre la forma { "error": "<código>", "detail": "…" }.

curl -s https://api.facturauno.com/api/v1/invoices \
  -H "X-Api-Key: $FACTURAUNO_KEY"

2. Emitir un comprobante issue

POST /api/v1/invoices encola la emisión y responde 202 de inmediato. El modelo es NETO: los unitPrice van sin IVA; el impuesto se calcula por ítem según su taxRateCode.

curl -s -X POST https://api.facturauno.com/api/v1/invoices \
  -H "X-Api-Key: $FACTURAUNO_KEY" -H "Content-Type: application/json" \
  -d '{
    "issuerId": "0b0e8a1e-1111-2222-3333-444455556666",
    "docClass": "INVOICE",
    "idempotencyKey": "pedido-84512",
    "currency": "PES",
    "recipient": {
      "docType": "CUIT",
      "docNumber": "30123456789",
      "name": "Cliente SRL",
      "email": "facturas@cliente.com",
      "fiscalProfile": { "condicionIVAReceptorId": 1 }
    },
    "items": [
      { "description": "Servicio de plataforma — julio", "qty": 1,
        "unitPrice": 100000.00, "taxRateCode": "IVA_21" }
    ],
    "ar": { "concepto": 2, "servicioDesde": "2026-07-01",
            "servicioHasta": "2026-07-31", "vtoPago": "2026-08-15" },
    "notifyEmail": "facturas@cliente.com",
    "webhookUrl": "https://miapp.com/hooks/facturauno"
  }'

Campos del body

CampoDetalle
issuerIdObligatorio. El emisor (empresa) por el que se factura.
emissionPointIdOpcional; si el emisor tiene un solo punto de emisión activo, se resuelve solo.
docClassINVOICE · CREDIT_NOTE · DEBIT_NOTE.
docTypeLocalOpcional. Override del tipo local (AR: CbteTipo "1"/"6"/"11"…). Sin él, el tipo se deriva de la condición fiscal emisor × receptor.
relatedInvoiceIdObligatorio en NC/ND: el comprobante que se ajusta (ver §4).
idempotencyKeyObligatorio, único por emisor. Reintentá con la misma key sin miedo (abajo).
externalRefOpcional. Tu referencia (id de pedido/cobro); vuelve en consultas y webhooks.
currencyCódigo de moneda del WS: PES (pesos AR) o DOL (dólares; requiere exchangeRate).
recipientPor referencia (recipientId ya validado) o inline: docType (AR: CUIT|CUIL|DNI|CF · PY: RUC con DV|CI|PASAPORTE), docNumber, name, email, address y fiscalProfile.condicionIVAReceptorId (RG 5616 — obligatoria en AR; ids en el catálogo). recipient: null = consumidor final innominado.
items[]description, qty, unitPrice NETO, taxRateCode (AR: IVA_21|IVA_10_5|IVA_27|IVA_5|IVA_2_5|IVA_0|EXENTO|NO_GRAVADO · PY: IVA_10|IVA_5|EXENTA|EXONERADO), discountAmount opcional.
pyOpciones PY: tipoTransaccion (iTipTra 1-13), condicionVenta CONTADO|CREDITO (+plazoCredito), indicadorPresencia (iIndPres) y motivoEmision (iMotEmi de NC/ND). Moneda PYG (entera) o USD (exchangeRate obligatorio). El comprobante autorizado devuelve el CDC de 44 dígitos en authCode y el PDF es el KuDE con QR verificable en e-Kuatia.
arOpciones AR: concepto (1=Productos, 2=Servicios, 3=Ambos — 2/3 exigen servicioDesde/servicioHasta), vtoPago, y condicionIVAReceptorId que pisa la del perfil si viene.
notifyEmailOpcional. Al autorizarse, el receptor recibe un mail con el link público.
webhookUrlOpcional. URL https adicional para los webhooks de ESTE comprobante (además de tus suscripciones).

Respuesta

HTTP/1.1 202 Accepted
{ "invoiceId": "6f2b…", "status": "QUEUED", "docTypeLocal": "1",
  "publicUrl": "https://facturauno.com/c/<token>" }
Idempotencia: repetir el POST con la misma idempotencyKey del mismo emisor devuelve 200 con el comprobante existente — no se re-emite ni se re-encola. Un canónico inválido responde 422 validation_failed con el detalle.

Estados del ciclo de vida: QUEUED → SENT → AUTHORIZED | REJECTED (rechazo de la autoridad) · VERIFYING (timeout en vuelo, se verifica antes de reintentar) · DEAD (agotó reintentos) · CANCELED.

3. Consultar issue · read

Detalle (con items y eventos)

curl -s https://api.facturauno.com/api/v1/invoices/{invoiceId} \
  -H "X-Api-Key: $FACTURAUNO_KEY"

Devuelve { invoice, items, events }: estado, número, authCode (CAE) y su vencimiento, totales, pdfStatus, publicUrl y el historial de transiciones.

Listado con filtros

curl -s "https://api.facturauno.com/api/v1/invoices?issuerId=…&status=AUTHORIZED&from=2026-07-01&page=1&pageSize=30" \
  -H "X-Api-Key: $FACTURAUNO_KEY"

Filtros: issuerId, country, status, docClass, search, from/to. Respuesta { total, items }.

XML / payload fiscal

curl -s https://api.facturauno.com/api/v1/invoices/{invoiceId}/xml \
  -H "X-Api-Key: $FACTURAUNO_KEY"

Solo con el comprobante AUTHORIZED; antes responde 409 conflict. El PDF no se descarga por la API: se sirve desde el link público cuando está materializado.

4. Notas de crédito / débito y anulación

Una NC o ND es un POST /api/v1/invoices más, con docClass: "CREDIT_NOTE" (o DEBIT_NOTE) y el relatedInvoiceId del comprobante que ajusta — obligatorio.

curl -s -X POST https://api.facturauno.com/api/v1/invoices \
  -H "X-Api-Key: $FACTURAUNO_KEY" -H "Content-Type: application/json" \
  -d '{ "issuerId": "…", "docClass": "CREDIT_NOTE",
        "relatedInvoiceId": "6f2b…", "idempotencyKey": "nc-pedido-84512",
        "currency": "PES", "recipient": { … }, "items": [ … ] }'
En Argentina no existe la cancelación: un comprobante autorizado se anula emitiendo la nota de crédito relacionada. POST /api/v1/invoices/{id}/cancel sobre un comprobante AR responde 409 use_credit_note a propósito. En Paraguay sí existe: el mismo endpoint (body opcional {"reason": "…"}) registra el evento de cancelación ante SIFEN — solo sobre un DTE AUTHORIZED y dentro de la ventana (48 h la Factura electrónica, 168 h NC/ND, desde la aprobación). Responde 202 con el evento PENDING; cuando SIFEN lo acepta, el comprobante pasa a CANCELED y te llega el webhook invoice.canceled. Vencida la ventana, anulá con nota de crédito como en AR.

5. Webhooks firmados issue

Suscribirse

curl -s -X POST https://api.facturauno.com/api/v1/webhooks/subscriptions \
  -H "X-Api-Key: $FACTURAUNO_KEY" -H "Content-Type: application/json" \
  -d '{ "url": "https://miapp.com/hooks/facturauno", "events": "*" }'
HTTP/1.1 200 OK
{ "subscriptionId": "…", "url": "https://miapp.com/hooks/facturauno", "events": "*",
  "secret": "<base64 — SE MUESTRA UNA SOLA VEZ, guardalo>",
  "signature": { "header": "X-FacturaUno-Signature",
                 "scheme": "t=<unix>,v1=hex(HMACSHA256(secret, '<unix>.' + body))",
                 "toleranceSeconds": 300 } }

El secret viaja en claro una única vez en esta respuesta y queda cifrado de nuestro lado. events acepta * o una lista separada por comas. GET /api/v1/webhooks/subscriptions lista las tuyas (sin secretos).

Eventos

EventoCuándo
invoice.authorizedLa autoridad fiscal autorizó (hay CAE y link público descargable).
invoice.rejectedLa autoridad rechazó el comprobante.
invoice.deadLa emisión agotó los reintentos (interviene soporte/admin).

Entrega y verificación de firma

Cada entrega es un POST JSON con headers X-FacturaUno-Event y X-FacturaUno-Signature: t=<unixSeconds>,v1=<hex>. Para verificar: reconstruí "<t>." + body crudo, calculá HMAC-SHA256 con tu secret, comparalo en tiempo constante con v1 y rechazá si |ahora − t| > 300 s.

{ "eventType": "invoice.authorized", "invoiceId": "6f2b…", "issuerId": "…",
  "country": "AR", "docClass": "INVOICE", "docTypeLocal": "1", "status": "AUTHORIZED",
  "number": 1234, "authCode": "75123456789012", "totalAmount": 121000.00,
  "currency": "PES", "externalRef": "pedido-84512", "idempotencyKey": "pedido-84512",
  "publicUrl": "https://facturauno.com/c/<token>", "at": "2026-08-07T14:03:22Z" }

Respondé 2xx; ante otra cosa reintentamos con backoff (1m, 5m, 15m, 1h, 4h…) hasta 24 h y después la entrega queda DEAD.