Saltar al contenido principal

Facturá desde tu código.

Una API REST para crear y emitir CFE, descargar el PDF y el XML firmado, y administrar contactos, productos, inventario y cobros. Vos mandás el JSON; la firma, el CAE y el envío a DGI corren de nuestro lado.

POST /api/v1/documentsAPI v1
{
  "documentTypeCode": 111,
  "receiverCode": "CLI-001",
  "paymentType": "CREDIT",
  "lines": [{
    "description": "Servicio mensual",
    "quantity": "1",
    "unitPrice": "10000,50",
    "billingIndicator": 3
  }],
  "autoIssue": true
}

→ 201 { "issue": { "enqueued": true } } ●

La misma capa que la app

Cada request corre sobre la misma capa de aplicación que la interfaz: mismas validaciones DGI, misma reserva de CAE, mismo pipeline de emisión. Lo que creás por API aparece en la app.

Idempotencia y reintentos

Clave de idempotencia en la creación y emisión idempotente por diseño: un retry devuelve lo ya creado. Diseñado para que un retry no duplique ni re-encole.

PDF y XML firmado

Cada CFE expone su representación imprimible y el XML firmado — el artefacto fiscal autoritativo — listos para descargar por API.

Errores tipados

Envelope estable con códigos máquina-legibles que no cambian entre versiones, detalle de campo cuando aplica y rate limits en headers estándar.

Empezá en cuatro pasos.

Registrás tu empresa, generás una key y emitís sobre la misma capa que usa la app.

  1. 01

    Creá tu cuenta

    Registrás tu empresa y configurás la emisión (certificado, CAE, numeración) desde la app.

  2. 02

    Generá tu API key

    En /app/configuracion/api-keys (rol OWNER o ADMIN). El secreto se muestra una sola vez: guardalo en un gestor de secretos.

  3. 03

    Autenticate

    Header Authorization: Bearer afk_live_… en cada request. La key resuelve la empresa: el tenant nunca viaja en la URL ni en el body.

  4. 04

    Creá y emití

    POST /documents con autoIssue: true crea el comprobante y encola la emisión. Sondeá GET /documents/{id} hasta ACCEPTED.

Una key por empresa

GET /api/v1/meafk_live_…
curl -s "https://facturar.uy/api/v1/me" \
  -H "Authorization: Bearer afk_live_…"

→ 200 {
  "company": { "legalName": "Estudio Méndez SRL" },
  "apiKey": { "prefix": "afk_live_a1b2c3" }
}

Las keys se crean en /app/configuracion/api-keys (roles OWNER o ADMIN); el secreto se muestra una única vez y solo se persiste su hash SHA-256. La key resuelve la empresa: los ids de otra empresa devuelven 404, y una key revocada o vencida devuelve 401 con el mismo mensaje que una inexistente.

Scopes por key

documents:read
Listar y consultar comprobantes; descargar PDF y XML firmado.
documents:write
Crear comprobantes y notas de corrección (NC/ND).
documents:issue
Emitir a DGI (POST /documents/{id}/issue y autoIssue).
contacts:read
Listar y consultar contactos.
contacts:write
Crear y actualizar contactos.
products:read
Listar y consultar productos.
products:write
Crear y actualizar productos.
inventory:read
Consultar stock y kardex.
inventory:write
Registrar movimientos manuales de stock.
payments:write
Registrar cobros, aplicar crédito a cuenta y anular cobros.
payments:read
Cuentas por cobrar (aging) y estado de cobro de comprobantes.

Cada operación de la referencia indica su scope. Operar sin el scope requerido devuelve 403 FORBIDDEN. Una key sin scopes (legada) tiene acceso total; GET /me no requiere ninguno.

Referencia de endpoints.

openapi.json · postman.json

Base URL
https://facturar.uy/api/v1

Identidad

Verificación de la API key y su empresa.

GET
/me
Identidad de la API key
—

Devuelve la empresa (id, nombre, razón social, RUC) y los metadatos de la key (nombre, prefijo). Úsalo como health check de la integración antes de operar. No requiere ningún scope: cualquier key válida puede llamarlo.

Respuesta 200 (application/json)
company
object | null
Empresa dueña de la API key (el tenant de todos los requests).
company.id
string
Id de la empresa.
company.name
string
Nombre de la empresa en la plataforma.
company.legalName
string
Razón social.
company.tradeName
string | null
Nombre comercial.
company.ruc
string | null
RUC de la empresa (string, 12 dígitos).
apiKey
object
Metadatos de la key autenticada (nunca incluye el secreto).
apiKey.name
string
Nombre dado a la key al crearla.
apiKey.prefix
string
Prefijo visible de la key (p.ej. afk_live_a1b2c3).

Documentos

Comprobantes fiscales electrónicos (CFE): listado, creación, emisión a DGI, NC/ND y artefactos (PDF / XML firmado).

GET
/documents
Listar comprobantes
documents:read

Listado de comprobantes de la empresa (emitidos y recibidos), más reciente primero. Nunca incluye plantillas de facturas recurrentes. Scope requerido: documents:read.

Parámetros
status
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY' · query
Filtra por estado del documento.
direction
'ISSUED' | 'RECEIVED' · query
Emitidos o recibidos.
type
integer · query
Código DGI del tipo de comprobante (p.ej. 101 e-Ticket, 111 e-Factura).
search
string · query
Búsqueda por receptor, tipo o número (mínimo 3 caracteres). Un valor solo numérico busca por número de comprobante, RUT/CI y número interno; «A-112» por serie y número.
limit
integer · query
Cantidad máxima de resultados (1-100).
offset
integer · query
Desplazamiento para paginar (0-10000). Para recorrer más allá usá paginación por cursor donde esté disponible.
cursor
string · query
Cursor opaco devuelto en pagination.nextCursor de la página anterior (paginación keyset, costo constante a cualquier profundidad). Excluyente con offset > 0.
createdAfter
string (fecha-hora) · query
Solo comprobantes creados DESPUÉS de este instante ISO-8601 (sincronización incremental).
updatedAfter
string (fecha-hora) · query
Solo comprobantes modificados DESPUÉS de este instante ISO-8601 — la forma eficiente de sondear cambios de estado (DGI, entrega) sin recorrer todo el listado.
includeTotal
boolean · query
false omite pagination.total y ahorra el conteo del lado del servidor (recomendado al paginar por cursor). Default true.
Respuesta 200 (application/json)
data *
object[]
Página de comprobantes (más recientes primero).
data[].id *
string
Id del comprobante (usarlo en GET /documents/{id}, /pdf, /xml, /issue).
data[].status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del ciclo de vida: DRAFT (borrador) → READY_TO_ISSUE → ISSUING → SIGNED → SUBMITTED → ACCEPTED/REJECTED/OBSERVED por DGI; CANCELLED (anulado antes de emitir), FAILED (falló tras reintentos), CONTINGENCY (emitido como CFC).
data[].direction *
'ISSUED' | 'RECEIVED'
ISSUED = emitido por tu empresa; RECEIVED = recibido de otro emisor electrónico (WS Intercambio).
data[].documentType *
object
Tipo de comprobante DGI (p.ej. 101 e-Ticket, 111 e-Factura).
data[].documentType.code
integer
Código DGI del tipo, p.ej. 101, 111, 112, 181, 182.
data[].documentType.name
string
Nombre del tipo de comprobante (snapshot informativo).
data[].documentType.kind
'CFE' | 'CFC'
CFE = comprobante electrónico normal (códigos 1xx); CFC = de contingencia (códigos 2xx).
data[].series
string | null
Serie fiscal (null en borradores).
data[].number
integer | null
Número fiscal (null hasta la reserva CAE).
data[].issueDate
string (fecha) | null
Fecha de emisión (YYYY-MM-DD); null en borradores sin fecha.
data[].currency *
string
Moneda ISO 4217 (UYU, USD, EUR, …).
data[].total *
string
Total del comprobante. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
data[].receiver
object | null
Contacto receptor; null para consumidor final o borradores sin receptor.
data[].receiver.legalName
string
Razón social o nombre del receptor.
data[].receiver.code
string | null
Código interno del contacto (Contact.code, único por empresa); null si el contacto no tiene código.
data[].receiverDeliveryStatus
'NOT_REQUIRED' | 'PENDING' | 'SENT' | 'FAILED' | 'SKIPPED'
Estado de entrega al receptor (email / WS Intercambio): NOT_REQUIRED (no aplica — sin receptor a quien entregar), PENDING (en cola), SENT (entregado), FAILED (falló tras reintentos), SKIPPED (existe receptor pero la entrega se omitió deliberadamente, p. ej. email deshabilitado).
data[].createdAt *
string (fecha-hora)
Momento de creación del registro (ISO 8601).
data[].updatedAt *
string (fecha-hora)
Última modificación del registro (ISO 8601).
pagination *
object
Paginación: total (opcional), limit, offset y nextCursor (keyset).
pagination.total
integer
Total de resultados que matchean los filtros. Presente salvo que el listado acepte includeTotal y se haya enviado false.
pagination.limit *
integer
Límite aplicado (1-100).
pagination.offset *
integer
Desplazamiento aplicado.
pagination.nextCursor
string | null
Cursor opaco para pedir la página siguiente con ?cursor=; null cuando no hay más resultados.
POST
/documents
Crear un comprobante
documents:write

Crea un comprobante en una llamada, con el mismo flujo que el sistema: la API compone los pasos del wizard (borrador → datos generales → líneas → datos adicionales) sobre la misma capa de aplicación que la UI. Nunca va directo a DGI: el resultado es un DRAFT normal, visible y editable en la app. Con autoIssue: true corre además el gate de validación DGI y encola el pipeline de emisión; si el borrador está incompleto responde 422 con el requisito exacto y details.documentId (el draft persiste y puede terminarse en la app). Idempotente vía header Idempotency-Key (o idempotencyKey en el body; el header gana): un retry devuelve el documento ya creado (200, reused: true). series/number se ignoran (la numeración la gestiona la reserva de CAE). Scope requerido: documents:write (más documents:issue cuando autoIssue: true).

Parámetros
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa). Un retry con la misma clave devuelve el documento ya creado sin duplicarlo.
Body (application/json)
documentTypeCode *
integer
Código DGI del tipo de comprobante (p.ej. 101 e-Ticket, 111 e-Factura).
branchCode
string
Código de sucursal (único por empresa). Si no se envía, se usa la sucursal por defecto de la empresa. Descubrí los códigos con GET /branches.
receiverCode
string
Código interno del contacto receptor (Contact.code, único por empresa); crealo antes con POST /contacts. Requerido para emitir, salvo consumidor final (receiverMode: "FINAL_CONSUMER").
receiverMode
'CONTACT' | 'FINAL_CONSUMER'
CONTACT = receptor identificado (requiere receiverCode); FINAL_CONSUMER = consumidor final (solo tipos e-Ticket).
currency
string
Moneda ISO 4217 (UYU, USD, EUR, …). Si no es UYU se requiere tipo de cambio al emitir.
exchangeRate
string
Tipo de cambio (string decimal) si la moneda no es UYU.
issueDate
string (fecha)
Fecha de emisión. Si se omite, se usa la fecha de hoy (zona horaria de la app) — igual que el wizard.
valueDate
string (fecha)
Fecha valor — solo e-Resguardo (182/282).
dueDate
string (fecha)
Fecha de vencimiento (típicamente con paymentType: "CREDIT").
periodFrom
string (fecha)
Período desde — operaciones de servicios.
periodTo
string (fecha)
Período hasta — operaciones de servicios.
paymentType
'CASH' | 'CREDIT' | 'NOT_APPLICABLE'
Condición de pago: CASH (contado), CREDIT (crédito) o NOT_APPLICABLE. Requerido para emitir en la mayoría de los tipos.
operationType
'SERVICE' | 'PRODUCT'
Tipo de operación: SERVICE (servicios) o PRODUCT (bienes). Requerido para emitir cuando hay receptor identificado.
ivaStatusIndicator
1 | 2 | 3
Indicador IVA al día (2/3 solo para tipos VCA).
professionalSecretIndicator
1 | 2
Indicador secreto profesional.
purchaseIdentificationNumber
string
Nº identificación compra.
notes
string
Notas internas — NO se envían a DGI.
adenda
string
Zona J Adenda — máx. 4 líneas × 96 caracteres.
additionalDocumentInfo
string
Información adicional del comprobante.
clause
string
Cláusula de venta / Incoterm (exportación), p.ej. "FOB".
saleModality
integer
Modalidad de venta (exportación): 1, 2, 3, 4, 80, 90, 91, 99.
transportRoute
integer
Vía de transporte (exportación): 1 marítimo, 2 aéreo, 3 terrestre, 8 N/A, 9 otro.
grossAmountIndicator
1 | 2 | 3
Indicador montos brutos: 1 = líneas con IVA incluido, 2 = IMEBA/adicionales incluidos, 3 = IVA mínimo/Monotributo. No corresponde (se guarda vacío) en e-Remito, e-Resguardo y e-Remito de Exportación (181/182/124).
thirdPartyPaymentsIndicator
1
Indicador pagos por cuenta de terceros.
ownCollectionIndicator
1
Indicador cobranza propia.
foreignCurrencyResaleIndicator
1 | 2
Indicador compra M/E para reventa (solo e-Boleta 151-153).
applyRoundingAdjustment
boolean
Ajuste por redondeo para ESTE comprobante (override de la configuración de la empresa; ausente = heredar). Con true, al validar/emitir se agrega la línea «Ajuste por redondeo» (indicador 6/7) que lleva el Monto a pagar al múltiplo; con false explícito no se redondea aunque la empresa lo tenga activado.
roundingMultiple
'1.00' | '0.50' | '0.10'
Múltiplo objetivo del Monto a pagar para el ajuste por redondeo de ESTE comprobante (ausente = heredar el de la empresa).
transferTypeIndicator
integer
Tipo de traslado de bienes (remitos): 1 venta, 2 traslados internos.
goodsOwnershipIndicator
integer
Indicador propiedad de la mercadería transportada (1 = cuenta ajena; requiere los campos goodsOwner*).
goodsOwnerDocumentType
integer
Tipo de documento del propietario.
goodsOwnerCountryCode
string
País del propietario (ISO 3166-1 alpha-2 o "99").
goodsOwnerDocumentNumberUy
string
Documento UY del propietario.
goodsOwnerDocumentNumberForeign
string
Documento extranjero del propietario.
goodsOwnerName
string
Nombre/razón social del propietario.
complement
object
Zona K — Complemento fiscal (mandante). Tipos de venta por cuenta ajena (131-143 / 231-243) y e-Boleta de entrada con compra de M/E para reventa por cuenta ajena (foreignCurrencyResaleIndicator = 2; mandante con NIE, RUT o NIFE). En otra e-Boleta la emisión se rechaza; en el resto de los tipos se ignora.
complement.mandanteDocumentType
string
Tipo de documento del mandante (numérico).
complement.mandanteCountryCode
string
País del mandante (ISO 3166-1 alpha-2 o "99").
complement.mandanteDocumentNumber
string
Número de documento del mandante.
complement.mandanteName
string
Nombre o denominación del mandante.
lines *
object[]
Líneas del comprobante (Zona B). Límite real según tipo de CFE (700 e-Ticket; 200 el resto).
lines[].description
string
Nombre del ítem. Requerido para emitir, salvo e-Resguardo (182/282).
lines[].quantity
string
Cantidad — string decimal, máx. 14 enteros y 3 decimales. Requerido para emitir, salvo e-Resguardo (182/282).
lines[].unit
string | null
Unidad de medida. Requerido para emitir, salvo e-Resguardo (182/282).
lines[].unitPrice *
string
Precio unitario — string decimal, máx. 11 enteros y 6 decimales.
lines[].billingIndicator
integer
Indicador de facturación (1-17; p.ej. 1 exento, 2 tasa mínima, 3 tasa básica). Requerido para emitir, salvo remitos y e-Resguardo. El rango válido depende del tipo de CFE. Para 2/3 el IVA se calcula automáticamente con las tasas vigentes si no se envía taxRate.
lines[].productCode
string
SKU del producto (Product.sku, único por empresa) para pre-cargar la línea y descontar stock si corresponde.
lines[].itemCodes
object[]
Códigos del ítem (hasta 5 pares). Los GTIN se validan con dígito verificador GS1.
lines[].itemCodes[].codeType *
string
Tipo de código: EAN/GTIN8/GTIN12/GTIN13/GTIN14 (con dígito verificador GS1), INT1/INT2 (interno), PLU, DUN14, etc.
lines[].itemCodes[].code *
string
Valor del código del ítem.
lines[].discountPercent
string
Descuento en porcentaje (string decimal).
lines[].discountAmount
string
Descuento en monto (string decimal).
lines[].surchargePercent
string
Recargo en porcentaje (string decimal).
lines[].surchargeAmount
string
Recargo en monto (string decimal).
lines[].taxRate
string
Tasa de IVA como fracción (p.ej. "0.22"). Solo requerida cuando el indicador de facturación es 4 (otra tasa); para 2/3 se deriva automáticamente de las tasas vigentes.
lines[].responsibleIndicator
string
B-C6: indicador Agente/Responsable ("R" o "A"). Obligatorio si la línea informa retenciones/percepciones, salvo en e-Resguardo (182/282), donde no corresponde.
lines[].retentionPerceptions
object[]
B-C20..B-C23: retenciones/percepciones/crédito fiscal de la línea (máx. 5). A-C125/A-C125.1 y la tabla Totales/RetencPercep se derivan de estas por código (2181xxx = crédito fiscal).
lines[].retentionPerceptions[].retentionCode *
string
B-C20: código DGI (numérico; 9999001-9999999 de libre uso).
lines[].retentionPerceptions[].rate *
string
B-C21: tasa (NUM 6, máx. 3 enteros y 3 decimales).
lines[].retentionPerceptions[].subjectAmount *
string
B-C22: monto sujeto a retención/percepción (> 0).
lines[].retentionPerceptions[].additionalInfo
string
B-C22.1: información adicional (opcional).
lines[].retentionPerceptions[].retentionValue *
string
B-C23: valor de la retención/percepción/crédito fiscal (> 0).
payments
object[]
Zona E (opcional).
payments[].lineNumber *
integer
Secuencia 1-based del medio de pago (máx. 40 por comprobante).
payments[].paymentMethodCode
integer
Código del medio de pago (catálogo DGI).
payments[].paymentMethodLabel *
string
Glosa del medio de pago.
payments[].printOrder
integer
Orden de impresión del medio de pago en el PDF (opcional).
payments[].amount *
string
Monto del pago. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
references
object[]
Zona F (obligatoria para NC/ND).
references[].lineNumber *
integer
Secuencia 1-based de la referencia.
references[].isGlobalReference
boolean
true = referencia global (requiere referenceReason y prohíbe los campos del comprobante específico).
references[].referencedDocumentTypeCode
integer
Código DGI del tipo del comprobante referenciado (p.ej. 111).
references[].referencedSeries
string
Serie del comprobante referenciado.
references[].referencedNumber
integer
Número del comprobante referenciado.
references[].referencedIssueDate
string (fecha-hora)
Fecha del comprobante referenciado (ISO 8601 con offset).
references[].referenceReason
string
Razón de la referencia — obligatoria cuando isGlobalReference es true.
references[].referencedAmount
string
Monto referenciado. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
references[].referenceType
integer
F-C2.1: tipo de referencia (código DGI). Opcional.
references[].referencedCurrency
string
Moneda del comprobante referenciado (ISO 4217, p.ej. UYU).
references[].referencedExchangeRate
string
Tipo de cambio del comprobante referenciado. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
adjustments
object[]
Zona D — descuentos/recargos globales (opcional).
adjustments[].lineNumber *
integer
Secuencia 1-based del ajuste.
adjustments[].movementType *
'DISCOUNT' | 'SURCHARGE'
Descuento o recargo global.
adjustments[].adjustmentType *
1 | 2
Tipo: 1 = monto ($), 2 = porcentaje (%).
adjustments[].description *
string
Glosa del descuento/recargo.
adjustments[].code
integer
Código del ajuste (catálogo DGI, opcional).
adjustments[].percent
string
Porcentaje (string decimal) cuando adjustmentType: 2.
adjustments[].amount
string
Monto (string decimal, > 0) cuando adjustmentType: 1. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
adjustments[].billingIndicator *
integer
Indicador de facturación del ajuste (1-17).
idempotencyKey
string
Clave de idempotencia (el header Idempotency-Key gana sobre este campo).
autoIssue
boolean
true → tras crear, corre el gate de validación DGI y encola la emisión (incluye el ajuste por redondeo automático si la empresa lo tiene activado). Requiere además el scope documents:issue.
Respuesta 201 (application/json)
document *
object
Detalle completo: identidad fiscal, totales (strings decimales), líneas (con sus retenciones/percepciones B-C20..B-C23), pagos, ajustes, referencias, CAE, tracking DGI, receptor y sucursal.
document.id *
string
Id del comprobante (usarlo en GET /documents/{id}, /pdf, /xml, /issue).
document.status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del ciclo de vida: DRAFT (borrador) → READY_TO_ISSUE → ISSUING → SIGNED → SUBMITTED → ACCEPTED/REJECTED/OBSERVED por DGI; CANCELLED (anulado antes de emitir), FAILED (falló tras reintentos), CONTINGENCY (emitido como CFC).
document.direction *
'ISSUED' | 'RECEIVED'
ISSUED = emitido por tu empresa; RECEIVED = recibido de otro emisor electrónico (WS Intercambio).
document.documentType *
object
Tipo de comprobante DGI (p.ej. 101 e-Ticket, 111 e-Factura).
document.documentType.code
integer
Código DGI del tipo, p.ej. 101, 111, 112, 181, 182.
document.documentType.name
string
Nombre del tipo de comprobante (snapshot informativo).
document.documentType.kind
'CFE' | 'CFC'
CFE = comprobante electrónico normal (códigos 1xx); CFC = de contingencia (códigos 2xx).
document.series
string | null
Serie fiscal (null en borradores).
document.number
integer | null
Número fiscal (null hasta la reserva CAE).
document.issueDate
string (fecha) | null
Fecha de emisión (YYYY-MM-DD); null en borradores sin fecha.
document.currency *
string
Moneda ISO 4217 (UYU, USD, EUR, …).
document.total *
string
Total del comprobante. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.receiver
object | null
Receptor identificado por su código interno; null para consumidor final.
document.receiver.legalName
string
Razón social o nombre del receptor.
document.receiver.code
string
Código interno del contacto (Contact.code, único por empresa; obligatorio).
document.receiverDeliveryStatus
'NOT_REQUIRED' | 'PENDING' | 'SENT' | 'FAILED' | 'SKIPPED'
Estado de entrega al receptor (email / WS Intercambio): NOT_REQUIRED (no aplica — sin receptor a quien entregar), PENDING (en cola), SENT (entregado), FAILED (falló tras reintentos), SKIPPED (existe receptor pero la entrega se omitió deliberadamente, p. ej. email deshabilitado).
document.createdAt *
string (fecha-hora)
Momento de creación del registro (ISO 8601).
document.updatedAt *
string (fecha-hora)
Última modificación del registro (ISO 8601).
document.exchangeRate
string | null
Tipo de cambio (string decimal) cuando la moneda no es UYU.
document.operationType
string | null
Tipo de operación: SERVICE (servicios) o PRODUCT (bienes); null si no se cargó.
document.paymentType
string | null
Condición de pago: CASH (contado), CREDIT (crédito) o NOT_APPLICABLE; null si no se cargó.
document.dueDate
string (fecha) | null
Fecha de vencimiento (YYYY-MM-DD); null si no aplica.
document.periodFrom
string (fecha) | null
Inicio del período facturado (servicios); null si no aplica.
document.periodTo
string (fecha) | null
Fin del período facturado (servicios); null si no aplica.
document.createdVia
'WIZARD' | 'API' | 'RECURRING' | 'INTEGRATION'
Origen de creación: WIZARD (app), API (esta API), RECURRING (regla recurrente), INTEGRATION (orden de canal de venta).
document.subtotal
string
Subtotal sin impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.taxTotal
string
Total de impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.totalAmount
string | null
Monto total del comprobante (string decimal, precisión DGI).
document.totalRetentionPerceptionAmount
string | null
Total retenciones/percepciones de encabezado.
document.totalFiscalCreditAmount
string | null
Total créditos fiscales de encabezado.
document.payableAmount
string | null
Monto a pagar.
document.receiverDocument
object | null
Identidad fiscal del receptor; null para consumidor final.
document.receiverDocument.legalName
string
Razón social del receptor.
document.receiverDocument.documentNumber
string | null
Número de documento fiscal del receptor (RUC, CI, etc.) como string — puede tener ceros a la izquierda.
document.branch
object | null
Sucursal emisora; null en borradores sin sucursal.
document.branch.code
string
Código de sucursal DGI (único por empresa).
document.branch.name
string | null
Nombre de la sucursal.
document.cae
object | null
Constancia de Autorización de Emisión asignada al comprobante; null si aún no hay reserva de numeración.
document.cae.number
string
Número de autorización CAE (11 dígitos DGI).
document.cae.series
string
Serie autorizada por el CAE.
document.cae.fromNumber
integer
Inicio del rango de numeración autorizado.
document.cae.toNumber
integer
Fin del rango de numeración autorizado.
document.cae.expirationDate
string (fecha)
Vencimiento del CAE (YYYY-MM-DD).
document.dgi
object
Tracking del envío a DGI.
document.dgi.trackingId
string | null
Id de seguimiento asignado por DGI al sobre; null hasta el envío.
document.dgi.submittedAt
string (fecha-hora) | null
Momento del envío a DGI (ISO 8601); null hasta el envío.
document.dgi.ackSobreCode
string | null
Estado del ACKSobre DGI: "AS" (sobre aceptado), "BS" (sobre rechazado) o "BA" (archivo rechazado); null hasta la respuesta.
document.lines
object[]
Líneas del comprobante (Zona B), en orden.
document.lines[].lineNumber
integer | null
Número de línea 1-based.
document.lines[].description
string
Nombre del ítem.
document.lines[].quantity
string
Cantidad. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].unit
string | null
Unidad de medida ("N/A" cuando no aplica).
document.lines[].unitPrice
string
Precio unitario. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].taxRate
string | null
Tasa de IVA aplicada como fracción (p.ej. "0.22"), null si no aplica.
document.lines[].billingIndicator
integer | null
Indicador de facturación.
document.lines[].productCode
string | null
SKU del producto referenciado (Product.sku, código único por empresa); null si la línea no referencia un producto del catálogo.
document.lines[].subtotal
string
Subtotal de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].tax
string
Impuesto de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].total
string
Total de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].retentionPerceptions
object[]
B-C20..B-C23 — retenciones/percepciones/crédito fiscal de la línea (máx. 5). A-C125/A-C125.1 y la tabla Totales se derivan de estas por código (los códigos 2181xxx son crédito fiscal).
document.lines[].retentionPerceptions[].code
string
B-C20: código DGI de retención/percepción/crédito fiscal.
document.lines[].retentionPerceptions[].rate
string | null
B-C21: tasa como porcentaje («5.000» = 5%). null cuando el CFE no la informa (opcional en el XSD).
document.lines[].retentionPerceptions[].subjectAmount
string
B-C22: monto sujeto a retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].retentionPerceptions[].additionalInfo
string | null
B-C22.1: información adicional.
document.lines[].retentionPerceptions[].retentionValue
string
B-C23: valor de la retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.payments
object[]
Zona E — medios de pago.
document.payments[].lineNumber
integer
Secuencia 1-based del medio de pago.
document.payments[].paymentMethodCode
integer | null
Código del medio de pago (catálogo DGI); null si no se informó.
document.payments[].paymentMethodLabel
string | null
Glosa del medio de pago.
document.payments[].amount
string
Monto del pago. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.adjustments
object[]
Zona D — descuentos/recargos globales, con el monto resuelto server-side.
document.adjustments[].lineNumber
integer
Secuencia 1-based del ajuste.
document.adjustments[].movementType
'DISCOUNT' | 'SURCHARGE'
DISCOUNT = descuento global; SURCHARGE = recargo global.
document.adjustments[].adjustmentType
integer | null
1 = monto, 2 = porcentaje.
document.adjustments[].description
string | null
Glosa del descuento/recargo.
document.adjustments[].code
integer | null
Código del ajuste (catálogo DGI); null si no se informó.
document.adjustments[].percent
string | null
Porcentaje (string decimal) cuando el tipo es 2; null en ajustes por monto.
document.adjustments[].amount
string | null
Monto monetario resuelto (también para ajustes porcentuales).
document.adjustments[].billingIndicator
integer | null
Indicador de facturación del ajuste (1-17).
document.references
object[]
Zona F — referencias a otros comprobantes (NC/ND).
document.references[].lineNumber
integer
Secuencia 1-based de la referencia.
document.references[].isGlobalReference
boolean
true = referencia global (sin comprobante específico; requiere referenceReason).
document.references[].referencedDocumentTypeCode
integer | null
Código DGI del tipo del comprobante referenciado.
document.references[].referencedSeries
string | null
Serie del comprobante referenciado.
document.references[].referencedNumber
integer | null
Número del comprobante referenciado.
document.references[].referencedIssueDate
string (fecha) | null
Fecha del comprobante referenciado (YYYY-MM-DD).
document.references[].referenceReason
string | null
Razón de la referencia (obligatoria si es global).
document.references[].referencedAmount
string | null
Monto referenciado (string decimal); null si no se informó.
reused *
boolean
true si un retry con la misma clave de idempotencia devolvió el documento ya creado (HTTP 200 en vez de 201).
issue
object | null
Resultado de la emisión cuando autoIssue: true; null en caso contrario.
issue.id *
string
Id del comprobante emitido/encolado.
issue.status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del comprobante al responder (READY_TO_ISSUE recién encolado; sondear GET /documents/{id}).
issue.enqueued *
boolean
true si esta llamada encoló el pipeline; false si ya estaba emitido o en curso (no-op idempotente).
GET
/documents/{id}
Detalle de un comprobante
documents:read

Detalle completo: identidad fiscal (tipo, serie, número), totales (strings decimales, nunca floats), líneas, CAE (número, serie, rango, vencimiento), tracking DGI (trackingId, submittedAt, ackSobreCode), receptor y sucursal. Tras emitir, sondear este endpoint hasta ACCEPTED / REJECTED / OBSERVED. Scope requerido: documents:read.

Parámetros
id *
string · path
Identificador del comprobante.
If-None-Match
string · header
ETag recibido en una respuesta anterior; si el comprobante no cambió la respuesta es 304 Not Modified sin body.
Respuesta 200 (application/json)
id *
string
Id del comprobante (usarlo en GET /documents/{id}, /pdf, /xml, /issue).
status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del ciclo de vida: DRAFT (borrador) → READY_TO_ISSUE → ISSUING → SIGNED → SUBMITTED → ACCEPTED/REJECTED/OBSERVED por DGI; CANCELLED (anulado antes de emitir), FAILED (falló tras reintentos), CONTINGENCY (emitido como CFC).
direction *
'ISSUED' | 'RECEIVED'
ISSUED = emitido por tu empresa; RECEIVED = recibido de otro emisor electrónico (WS Intercambio).
documentType *
object
Tipo de comprobante DGI (p.ej. 101 e-Ticket, 111 e-Factura).
documentType.code
integer
Código DGI del tipo, p.ej. 101, 111, 112, 181, 182.
documentType.name
string
Nombre del tipo de comprobante (snapshot informativo).
documentType.kind
'CFE' | 'CFC'
CFE = comprobante electrónico normal (códigos 1xx); CFC = de contingencia (códigos 2xx).
series
string | null
Serie fiscal (null en borradores).
number
integer | null
Número fiscal (null hasta la reserva CAE).
issueDate
string (fecha) | null
Fecha de emisión (YYYY-MM-DD); null en borradores sin fecha.
currency *
string
Moneda ISO 4217 (UYU, USD, EUR, …).
total *
string
Total del comprobante. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
receiver
object | null
Receptor identificado por su código interno; null para consumidor final.
receiver.legalName
string
Razón social o nombre del receptor.
receiver.code
string
Código interno del contacto (Contact.code, único por empresa; obligatorio).
receiverDeliveryStatus
'NOT_REQUIRED' | 'PENDING' | 'SENT' | 'FAILED' | 'SKIPPED'
Estado de entrega al receptor (email / WS Intercambio): NOT_REQUIRED (no aplica — sin receptor a quien entregar), PENDING (en cola), SENT (entregado), FAILED (falló tras reintentos), SKIPPED (existe receptor pero la entrega se omitió deliberadamente, p. ej. email deshabilitado).
createdAt *
string (fecha-hora)
Momento de creación del registro (ISO 8601).
updatedAt *
string (fecha-hora)
Última modificación del registro (ISO 8601).
exchangeRate
string | null
Tipo de cambio (string decimal) cuando la moneda no es UYU.
operationType
string | null
Tipo de operación: SERVICE (servicios) o PRODUCT (bienes); null si no se cargó.
paymentType
string | null
Condición de pago: CASH (contado), CREDIT (crédito) o NOT_APPLICABLE; null si no se cargó.
dueDate
string (fecha) | null
Fecha de vencimiento (YYYY-MM-DD); null si no aplica.
periodFrom
string (fecha) | null
Inicio del período facturado (servicios); null si no aplica.
periodTo
string (fecha) | null
Fin del período facturado (servicios); null si no aplica.
createdVia
'WIZARD' | 'API' | 'RECURRING' | 'INTEGRATION'
Origen de creación: WIZARD (app), API (esta API), RECURRING (regla recurrente), INTEGRATION (orden de canal de venta).
subtotal
string
Subtotal sin impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
taxTotal
string
Total de impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
totalAmount
string | null
Monto total del comprobante (string decimal, precisión DGI).
totalRetentionPerceptionAmount
string | null
Total retenciones/percepciones de encabezado.
totalFiscalCreditAmount
string | null
Total créditos fiscales de encabezado.
payableAmount
string | null
Monto a pagar.
receiverDocument
object | null
Identidad fiscal del receptor; null para consumidor final.
receiverDocument.legalName
string
Razón social del receptor.
receiverDocument.documentNumber
string | null
Número de documento fiscal del receptor (RUC, CI, etc.) como string — puede tener ceros a la izquierda.
branch
object | null
Sucursal emisora; null en borradores sin sucursal.
branch.code
string
Código de sucursal DGI (único por empresa).
branch.name
string | null
Nombre de la sucursal.
cae
object | null
Constancia de Autorización de Emisión asignada al comprobante; null si aún no hay reserva de numeración.
cae.number
string
Número de autorización CAE (11 dígitos DGI).
cae.series
string
Serie autorizada por el CAE.
cae.fromNumber
integer
Inicio del rango de numeración autorizado.
cae.toNumber
integer
Fin del rango de numeración autorizado.
cae.expirationDate
string (fecha)
Vencimiento del CAE (YYYY-MM-DD).
dgi
object
Tracking del envío a DGI.
dgi.trackingId
string | null
Id de seguimiento asignado por DGI al sobre; null hasta el envío.
dgi.submittedAt
string (fecha-hora) | null
Momento del envío a DGI (ISO 8601); null hasta el envío.
dgi.ackSobreCode
string | null
Estado del ACKSobre DGI: "AS" (sobre aceptado), "BS" (sobre rechazado) o "BA" (archivo rechazado); null hasta la respuesta.
lines
object[]
Líneas del comprobante (Zona B), en orden.
lines[].lineNumber
integer | null
Número de línea 1-based.
lines[].description
string
Nombre del ítem.
lines[].quantity
string
Cantidad. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
lines[].unit
string | null
Unidad de medida ("N/A" cuando no aplica).
lines[].unitPrice
string
Precio unitario. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
lines[].taxRate
string | null
Tasa de IVA aplicada como fracción (p.ej. "0.22"), null si no aplica.
lines[].billingIndicator
integer | null
Indicador de facturación.
lines[].productCode
string | null
SKU del producto referenciado (Product.sku, código único por empresa); null si la línea no referencia un producto del catálogo.
lines[].subtotal
string
Subtotal de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
lines[].tax
string
Impuesto de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
lines[].total
string
Total de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
lines[].retentionPerceptions
object[]
B-C20..B-C23 — retenciones/percepciones/crédito fiscal de la línea (máx. 5). A-C125/A-C125.1 y la tabla Totales se derivan de estas por código (los códigos 2181xxx son crédito fiscal).
lines[].retentionPerceptions[].code
string
B-C20: código DGI de retención/percepción/crédito fiscal.
lines[].retentionPerceptions[].rate
string | null
B-C21: tasa como porcentaje («5.000» = 5%). null cuando el CFE no la informa (opcional en el XSD).
lines[].retentionPerceptions[].subjectAmount
string
B-C22: monto sujeto a retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
lines[].retentionPerceptions[].additionalInfo
string | null
B-C22.1: información adicional.
lines[].retentionPerceptions[].retentionValue
string
B-C23: valor de la retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
payments
object[]
Zona E — medios de pago.
payments[].lineNumber
integer
Secuencia 1-based del medio de pago.
payments[].paymentMethodCode
integer | null
Código del medio de pago (catálogo DGI); null si no se informó.
payments[].paymentMethodLabel
string | null
Glosa del medio de pago.
payments[].amount
string
Monto del pago. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
adjustments
object[]
Zona D — descuentos/recargos globales, con el monto resuelto server-side.
adjustments[].lineNumber
integer
Secuencia 1-based del ajuste.
adjustments[].movementType
'DISCOUNT' | 'SURCHARGE'
DISCOUNT = descuento global; SURCHARGE = recargo global.
adjustments[].adjustmentType
integer | null
1 = monto, 2 = porcentaje.
adjustments[].description
string | null
Glosa del descuento/recargo.
adjustments[].code
integer | null
Código del ajuste (catálogo DGI); null si no se informó.
adjustments[].percent
string | null
Porcentaje (string decimal) cuando el tipo es 2; null en ajustes por monto.
adjustments[].amount
string | null
Monto monetario resuelto (también para ajustes porcentuales).
adjustments[].billingIndicator
integer | null
Indicador de facturación del ajuste (1-17).
references
object[]
Zona F — referencias a otros comprobantes (NC/ND).
references[].lineNumber
integer
Secuencia 1-based de la referencia.
references[].isGlobalReference
boolean
true = referencia global (sin comprobante específico; requiere referenceReason).
references[].referencedDocumentTypeCode
integer | null
Código DGI del tipo del comprobante referenciado.
references[].referencedSeries
string | null
Serie del comprobante referenciado.
references[].referencedNumber
integer | null
Número del comprobante referenciado.
references[].referencedIssueDate
string (fecha) | null
Fecha del comprobante referenciado (YYYY-MM-DD).
references[].referenceReason
string | null
Razón de la referencia (obligatoria si es global).
references[].referencedAmount
string | null
Monto referenciado (string decimal); null si no se informó.
POST
/documents/{id}/issue
Emitir a DGI
documents:issue

Dispara la emisión a DGI. READY_TO_ISSUE → encola el pipeline (202, enqueued: true). DRAFT → corre primero la misma validación de completitud que la UI (campos DGI, snapshots fiscales, reserva de numeración CAE, tipo de cambio); si el borrador está incompleto responde 422 con el requisito exacto. Si la empresa tiene activado el ajuste por redondeo automático, al validar un DRAFT se agrega/recalcula la línea «Ajuste por redondeo» (indicador de facturación 6/7) que lleva el Monto a pagar al múltiplo configurado. Ya emitido o en curso → idempotente: 200 con enqueued: false y el estado actual (el jobId de BullMQ deriva del documento, sin doble-encolado). FAILED → reintento: resetea a READY_TO_ISSUE (mismo use case que el botón «Reintentar» de la app, auditado) y re-encola (202). CANCELLED → 409. El resultado es asíncrono: sondear GET /documents/{id}. Scope requerido: documents:issue.

Parámetros
id *
string · path
Identificador del comprobante a emitir.
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Respuesta 202 (application/json)
id *
string
Id del comprobante emitido/encolado.
status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del comprobante al responder (READY_TO_ISSUE recién encolado; sondear GET /documents/{id}).
enqueued *
boolean
true si esta llamada encoló el pipeline; false si ya estaba emitido o en curso (no-op idempotente).
POST
/documents/{id}/correction-note
Crear NC/ND (nota de corrección)
documents:write

Crea una nota de crédito o débito a partir de un comprobante ACCEPTED/OBSERVED, pre-llenada desde el origen (cabecera, líneas, pagos, retenciones) y con la referencia Zona F al documento fuente puesta automáticamente (monto acotado al total a pagar del origen). La NC resta o anula el comprobante original (devoluciones, descuentos, corrección de errores — es la forma fiscalmente correcta de anular un CFE en Uruguay: no existe el "delete"); la ND suma o aumenta su valor (gastos extra, diferencias de precio, intereses). correctionTypeCode debe ser la NC o ND que corresponde al tipo del documento fuente (p.ej. 112 NC de e-Factura). Scope requerido: documents:write (más documents:issue cuando autoIssue: true).

Parámetros
id *
string · path
Identificador del comprobante origen (ACCEPTED u OBSERVED).
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
correctionTypeCode *
integer
Código de la NC/ND correspondiente al tipo del documento fuente (p.ej. 112 NC de e-Factura, 113 ND de e-Factura).
autoIssue
boolean
true → emite la nota inmediatamente (requiere además documents:issue).
Respuesta 201 (application/json)
document *
object
Detalle completo: identidad fiscal, totales (strings decimales), líneas (con sus retenciones/percepciones B-C20..B-C23), pagos, ajustes, referencias, CAE, tracking DGI, receptor y sucursal.
document.id *
string
Id del comprobante (usarlo en GET /documents/{id}, /pdf, /xml, /issue).
document.status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del ciclo de vida: DRAFT (borrador) → READY_TO_ISSUE → ISSUING → SIGNED → SUBMITTED → ACCEPTED/REJECTED/OBSERVED por DGI; CANCELLED (anulado antes de emitir), FAILED (falló tras reintentos), CONTINGENCY (emitido como CFC).
document.direction *
'ISSUED' | 'RECEIVED'
ISSUED = emitido por tu empresa; RECEIVED = recibido de otro emisor electrónico (WS Intercambio).
document.documentType *
object
Tipo de comprobante DGI (p.ej. 101 e-Ticket, 111 e-Factura).
document.documentType.code
integer
Código DGI del tipo, p.ej. 101, 111, 112, 181, 182.
document.documentType.name
string
Nombre del tipo de comprobante (snapshot informativo).
document.documentType.kind
'CFE' | 'CFC'
CFE = comprobante electrónico normal (códigos 1xx); CFC = de contingencia (códigos 2xx).
document.series
string | null
Serie fiscal (null en borradores).
document.number
integer | null
Número fiscal (null hasta la reserva CAE).
document.issueDate
string (fecha) | null
Fecha de emisión (YYYY-MM-DD); null en borradores sin fecha.
document.currency *
string
Moneda ISO 4217 (UYU, USD, EUR, …).
document.total *
string
Total del comprobante. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.receiver
object | null
Receptor identificado por su código interno; null para consumidor final.
document.receiver.legalName
string
Razón social o nombre del receptor.
document.receiver.code
string
Código interno del contacto (Contact.code, único por empresa; obligatorio).
document.receiverDeliveryStatus
'NOT_REQUIRED' | 'PENDING' | 'SENT' | 'FAILED' | 'SKIPPED'
Estado de entrega al receptor (email / WS Intercambio): NOT_REQUIRED (no aplica — sin receptor a quien entregar), PENDING (en cola), SENT (entregado), FAILED (falló tras reintentos), SKIPPED (existe receptor pero la entrega se omitió deliberadamente, p. ej. email deshabilitado).
document.createdAt *
string (fecha-hora)
Momento de creación del registro (ISO 8601).
document.updatedAt *
string (fecha-hora)
Última modificación del registro (ISO 8601).
document.exchangeRate
string | null
Tipo de cambio (string decimal) cuando la moneda no es UYU.
document.operationType
string | null
Tipo de operación: SERVICE (servicios) o PRODUCT (bienes); null si no se cargó.
document.paymentType
string | null
Condición de pago: CASH (contado), CREDIT (crédito) o NOT_APPLICABLE; null si no se cargó.
document.dueDate
string (fecha) | null
Fecha de vencimiento (YYYY-MM-DD); null si no aplica.
document.periodFrom
string (fecha) | null
Inicio del período facturado (servicios); null si no aplica.
document.periodTo
string (fecha) | null
Fin del período facturado (servicios); null si no aplica.
document.createdVia
'WIZARD' | 'API' | 'RECURRING' | 'INTEGRATION'
Origen de creación: WIZARD (app), API (esta API), RECURRING (regla recurrente), INTEGRATION (orden de canal de venta).
document.subtotal
string
Subtotal sin impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.taxTotal
string
Total de impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.totalAmount
string | null
Monto total del comprobante (string decimal, precisión DGI).
document.totalRetentionPerceptionAmount
string | null
Total retenciones/percepciones de encabezado.
document.totalFiscalCreditAmount
string | null
Total créditos fiscales de encabezado.
document.payableAmount
string | null
Monto a pagar.
document.receiverDocument
object | null
Identidad fiscal del receptor; null para consumidor final.
document.receiverDocument.legalName
string
Razón social del receptor.
document.receiverDocument.documentNumber
string | null
Número de documento fiscal del receptor (RUC, CI, etc.) como string — puede tener ceros a la izquierda.
document.branch
object | null
Sucursal emisora; null en borradores sin sucursal.
document.branch.code
string
Código de sucursal DGI (único por empresa).
document.branch.name
string | null
Nombre de la sucursal.
document.cae
object | null
Constancia de Autorización de Emisión asignada al comprobante; null si aún no hay reserva de numeración.
document.cae.number
string
Número de autorización CAE (11 dígitos DGI).
document.cae.series
string
Serie autorizada por el CAE.
document.cae.fromNumber
integer
Inicio del rango de numeración autorizado.
document.cae.toNumber
integer
Fin del rango de numeración autorizado.
document.cae.expirationDate
string (fecha)
Vencimiento del CAE (YYYY-MM-DD).
document.dgi
object
Tracking del envío a DGI.
document.dgi.trackingId
string | null
Id de seguimiento asignado por DGI al sobre; null hasta el envío.
document.dgi.submittedAt
string (fecha-hora) | null
Momento del envío a DGI (ISO 8601); null hasta el envío.
document.dgi.ackSobreCode
string | null
Estado del ACKSobre DGI: "AS" (sobre aceptado), "BS" (sobre rechazado) o "BA" (archivo rechazado); null hasta la respuesta.
document.lines
object[]
Líneas del comprobante (Zona B), en orden.
document.lines[].lineNumber
integer | null
Número de línea 1-based.
document.lines[].description
string
Nombre del ítem.
document.lines[].quantity
string
Cantidad. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].unit
string | null
Unidad de medida ("N/A" cuando no aplica).
document.lines[].unitPrice
string
Precio unitario. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].taxRate
string | null
Tasa de IVA aplicada como fracción (p.ej. "0.22"), null si no aplica.
document.lines[].billingIndicator
integer | null
Indicador de facturación.
document.lines[].productCode
string | null
SKU del producto referenciado (Product.sku, código único por empresa); null si la línea no referencia un producto del catálogo.
document.lines[].subtotal
string
Subtotal de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].tax
string
Impuesto de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].total
string
Total de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].retentionPerceptions
object[]
B-C20..B-C23 — retenciones/percepciones/crédito fiscal de la línea (máx. 5). A-C125/A-C125.1 y la tabla Totales se derivan de estas por código (los códigos 2181xxx son crédito fiscal).
document.lines[].retentionPerceptions[].code
string
B-C20: código DGI de retención/percepción/crédito fiscal.
document.lines[].retentionPerceptions[].rate
string | null
B-C21: tasa como porcentaje («5.000» = 5%). null cuando el CFE no la informa (opcional en el XSD).
document.lines[].retentionPerceptions[].subjectAmount
string
B-C22: monto sujeto a retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].retentionPerceptions[].additionalInfo
string | null
B-C22.1: información adicional.
document.lines[].retentionPerceptions[].retentionValue
string
B-C23: valor de la retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.payments
object[]
Zona E — medios de pago.
document.payments[].lineNumber
integer
Secuencia 1-based del medio de pago.
document.payments[].paymentMethodCode
integer | null
Código del medio de pago (catálogo DGI); null si no se informó.
document.payments[].paymentMethodLabel
string | null
Glosa del medio de pago.
document.payments[].amount
string
Monto del pago. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.adjustments
object[]
Zona D — descuentos/recargos globales, con el monto resuelto server-side.
document.adjustments[].lineNumber
integer
Secuencia 1-based del ajuste.
document.adjustments[].movementType
'DISCOUNT' | 'SURCHARGE'
DISCOUNT = descuento global; SURCHARGE = recargo global.
document.adjustments[].adjustmentType
integer | null
1 = monto, 2 = porcentaje.
document.adjustments[].description
string | null
Glosa del descuento/recargo.
document.adjustments[].code
integer | null
Código del ajuste (catálogo DGI); null si no se informó.
document.adjustments[].percent
string | null
Porcentaje (string decimal) cuando el tipo es 2; null en ajustes por monto.
document.adjustments[].amount
string | null
Monto monetario resuelto (también para ajustes porcentuales).
document.adjustments[].billingIndicator
integer | null
Indicador de facturación del ajuste (1-17).
document.references
object[]
Zona F — referencias a otros comprobantes (NC/ND).
document.references[].lineNumber
integer
Secuencia 1-based de la referencia.
document.references[].isGlobalReference
boolean
true = referencia global (sin comprobante específico; requiere referenceReason).
document.references[].referencedDocumentTypeCode
integer | null
Código DGI del tipo del comprobante referenciado.
document.references[].referencedSeries
string | null
Serie del comprobante referenciado.
document.references[].referencedNumber
integer | null
Número del comprobante referenciado.
document.references[].referencedIssueDate
string (fecha) | null
Fecha del comprobante referenciado (YYYY-MM-DD).
document.references[].referenceReason
string | null
Razón de la referencia (obligatoria si es global).
document.references[].referencedAmount
string | null
Monto referenciado (string decimal); null si no se informó.
issue
object | null
Resultado de la emisión cuando autoIssue: true; null en caso contrario.
issue.id *
string
Id del comprobante emitido/encolado.
issue.status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del comprobante al responder (READY_TO_ISSUE recién encolado; sondear GET /documents/{id}).
issue.enqueued *
boolean
true si esta llamada encoló el pipeline; false si ya estaba emitido o en curso (no-op idempotente).
POST
/documents/{id}/collection-receipt
Crear recibo electrónico
documents:write

Crea un recibo de cobranza a partir de una e-Factura (111) o e-Ticket (101) en estado ACCEPTED u OBSERVED, según la FAQ 4.10 de DGI (Res. Nº 303/2019): un CFE del mismo tipo con indicador de cobranza propia (A-C20 = 1), una línea «COBRANZA DE …» por cada ítem del original como monto no facturable (B-C4 = 6, IVA incluido en el importe) y la referencia en Zona F al comprobante cobrado. El borrador puede ajustarse (p.ej. cobranza parcial) antes de emitir, o emitirse directo con autoIssue. Scope requerido: documents:write (+ documents:issue si autoIssue).

Parámetros
id *
string · path
Identificador del comprobante origen (e-Factura/e-Ticket ACCEPTED u OBSERVED).
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
autoIssue
boolean
true → emite el recibo inmediatamente (requiere además documents:issue).
Respuesta 201 (application/json)
document *
object
Detalle completo: identidad fiscal, totales (strings decimales), líneas (con sus retenciones/percepciones B-C20..B-C23), pagos, ajustes, referencias, CAE, tracking DGI, receptor y sucursal.
document.id *
string
Id del comprobante (usarlo en GET /documents/{id}, /pdf, /xml, /issue).
document.status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del ciclo de vida: DRAFT (borrador) → READY_TO_ISSUE → ISSUING → SIGNED → SUBMITTED → ACCEPTED/REJECTED/OBSERVED por DGI; CANCELLED (anulado antes de emitir), FAILED (falló tras reintentos), CONTINGENCY (emitido como CFC).
document.direction *
'ISSUED' | 'RECEIVED'
ISSUED = emitido por tu empresa; RECEIVED = recibido de otro emisor electrónico (WS Intercambio).
document.documentType *
object
Tipo de comprobante DGI (p.ej. 101 e-Ticket, 111 e-Factura).
document.documentType.code
integer
Código DGI del tipo, p.ej. 101, 111, 112, 181, 182.
document.documentType.name
string
Nombre del tipo de comprobante (snapshot informativo).
document.documentType.kind
'CFE' | 'CFC'
CFE = comprobante electrónico normal (códigos 1xx); CFC = de contingencia (códigos 2xx).
document.series
string | null
Serie fiscal (null en borradores).
document.number
integer | null
Número fiscal (null hasta la reserva CAE).
document.issueDate
string (fecha) | null
Fecha de emisión (YYYY-MM-DD); null en borradores sin fecha.
document.currency *
string
Moneda ISO 4217 (UYU, USD, EUR, …).
document.total *
string
Total del comprobante. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.receiver
object | null
Receptor identificado por su código interno; null para consumidor final.
document.receiver.legalName
string
Razón social o nombre del receptor.
document.receiver.code
string
Código interno del contacto (Contact.code, único por empresa; obligatorio).
document.receiverDeliveryStatus
'NOT_REQUIRED' | 'PENDING' | 'SENT' | 'FAILED' | 'SKIPPED'
Estado de entrega al receptor (email / WS Intercambio): NOT_REQUIRED (no aplica — sin receptor a quien entregar), PENDING (en cola), SENT (entregado), FAILED (falló tras reintentos), SKIPPED (existe receptor pero la entrega se omitió deliberadamente, p. ej. email deshabilitado).
document.createdAt *
string (fecha-hora)
Momento de creación del registro (ISO 8601).
document.updatedAt *
string (fecha-hora)
Última modificación del registro (ISO 8601).
document.exchangeRate
string | null
Tipo de cambio (string decimal) cuando la moneda no es UYU.
document.operationType
string | null
Tipo de operación: SERVICE (servicios) o PRODUCT (bienes); null si no se cargó.
document.paymentType
string | null
Condición de pago: CASH (contado), CREDIT (crédito) o NOT_APPLICABLE; null si no se cargó.
document.dueDate
string (fecha) | null
Fecha de vencimiento (YYYY-MM-DD); null si no aplica.
document.periodFrom
string (fecha) | null
Inicio del período facturado (servicios); null si no aplica.
document.periodTo
string (fecha) | null
Fin del período facturado (servicios); null si no aplica.
document.createdVia
'WIZARD' | 'API' | 'RECURRING' | 'INTEGRATION'
Origen de creación: WIZARD (app), API (esta API), RECURRING (regla recurrente), INTEGRATION (orden de canal de venta).
document.subtotal
string
Subtotal sin impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.taxTotal
string
Total de impuestos. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.totalAmount
string | null
Monto total del comprobante (string decimal, precisión DGI).
document.totalRetentionPerceptionAmount
string | null
Total retenciones/percepciones de encabezado.
document.totalFiscalCreditAmount
string | null
Total créditos fiscales de encabezado.
document.payableAmount
string | null
Monto a pagar.
document.receiverDocument
object | null
Identidad fiscal del receptor; null para consumidor final.
document.receiverDocument.legalName
string
Razón social del receptor.
document.receiverDocument.documentNumber
string | null
Número de documento fiscal del receptor (RUC, CI, etc.) como string — puede tener ceros a la izquierda.
document.branch
object | null
Sucursal emisora; null en borradores sin sucursal.
document.branch.code
string
Código de sucursal DGI (único por empresa).
document.branch.name
string | null
Nombre de la sucursal.
document.cae
object | null
Constancia de Autorización de Emisión asignada al comprobante; null si aún no hay reserva de numeración.
document.cae.number
string
Número de autorización CAE (11 dígitos DGI).
document.cae.series
string
Serie autorizada por el CAE.
document.cae.fromNumber
integer
Inicio del rango de numeración autorizado.
document.cae.toNumber
integer
Fin del rango de numeración autorizado.
document.cae.expirationDate
string (fecha)
Vencimiento del CAE (YYYY-MM-DD).
document.dgi
object
Tracking del envío a DGI.
document.dgi.trackingId
string | null
Id de seguimiento asignado por DGI al sobre; null hasta el envío.
document.dgi.submittedAt
string (fecha-hora) | null
Momento del envío a DGI (ISO 8601); null hasta el envío.
document.dgi.ackSobreCode
string | null
Estado del ACKSobre DGI: "AS" (sobre aceptado), "BS" (sobre rechazado) o "BA" (archivo rechazado); null hasta la respuesta.
document.lines
object[]
Líneas del comprobante (Zona B), en orden.
document.lines[].lineNumber
integer | null
Número de línea 1-based.
document.lines[].description
string
Nombre del ítem.
document.lines[].quantity
string
Cantidad. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].unit
string | null
Unidad de medida ("N/A" cuando no aplica).
document.lines[].unitPrice
string
Precio unitario. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].taxRate
string | null
Tasa de IVA aplicada como fracción (p.ej. "0.22"), null si no aplica.
document.lines[].billingIndicator
integer | null
Indicador de facturación.
document.lines[].productCode
string | null
SKU del producto referenciado (Product.sku, código único por empresa); null si la línea no referencia un producto del catálogo.
document.lines[].subtotal
string
Subtotal de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].tax
string
Impuesto de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].total
string
Total de la línea. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].retentionPerceptions
object[]
B-C20..B-C23 — retenciones/percepciones/crédito fiscal de la línea (máx. 5). A-C125/A-C125.1 y la tabla Totales se derivan de estas por código (los códigos 2181xxx son crédito fiscal).
document.lines[].retentionPerceptions[].code
string
B-C20: código DGI de retención/percepción/crédito fiscal.
document.lines[].retentionPerceptions[].rate
string | null
B-C21: tasa como porcentaje («5.000» = 5%). null cuando el CFE no la informa (opcional en el XSD).
document.lines[].retentionPerceptions[].subjectAmount
string
B-C22: monto sujeto a retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.lines[].retentionPerceptions[].additionalInfo
string | null
B-C22.1: información adicional.
document.lines[].retentionPerceptions[].retentionValue
string
B-C23: valor de la retención/percepción/crédito fiscal. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.payments
object[]
Zona E — medios de pago.
document.payments[].lineNumber
integer
Secuencia 1-based del medio de pago.
document.payments[].paymentMethodCode
integer | null
Código del medio de pago (catálogo DGI); null si no se informó.
document.payments[].paymentMethodLabel
string | null
Glosa del medio de pago.
document.payments[].amount
string
Monto del pago. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
document.adjustments
object[]
Zona D — descuentos/recargos globales, con el monto resuelto server-side.
document.adjustments[].lineNumber
integer
Secuencia 1-based del ajuste.
document.adjustments[].movementType
'DISCOUNT' | 'SURCHARGE'
DISCOUNT = descuento global; SURCHARGE = recargo global.
document.adjustments[].adjustmentType
integer | null
1 = monto, 2 = porcentaje.
document.adjustments[].description
string | null
Glosa del descuento/recargo.
document.adjustments[].code
integer | null
Código del ajuste (catálogo DGI); null si no se informó.
document.adjustments[].percent
string | null
Porcentaje (string decimal) cuando el tipo es 2; null en ajustes por monto.
document.adjustments[].amount
string | null
Monto monetario resuelto (también para ajustes porcentuales).
document.adjustments[].billingIndicator
integer | null
Indicador de facturación del ajuste (1-17).
document.references
object[]
Zona F — referencias a otros comprobantes (NC/ND).
document.references[].lineNumber
integer
Secuencia 1-based de la referencia.
document.references[].isGlobalReference
boolean
true = referencia global (sin comprobante específico; requiere referenceReason).
document.references[].referencedDocumentTypeCode
integer | null
Código DGI del tipo del comprobante referenciado.
document.references[].referencedSeries
string | null
Serie del comprobante referenciado.
document.references[].referencedNumber
integer | null
Número del comprobante referenciado.
document.references[].referencedIssueDate
string (fecha) | null
Fecha del comprobante referenciado (YYYY-MM-DD).
document.references[].referenceReason
string | null
Razón de la referencia (obligatoria si es global).
document.references[].referencedAmount
string | null
Monto referenciado (string decimal); null si no se informó.
issue
object | null
Resultado de la emisión cuando autoIssue: true; null en caso contrario.
issue.id *
string
Id del comprobante emitido/encolado.
issue.status *
'DRAFT' | 'READY_TO_ISSUE' | 'ISSUING' | 'SIGNED' | 'SUBMITTED' | 'ACCEPTED' | 'REJECTED' | 'OBSERVED' | 'CANCELLED' | 'FAILED' | 'CONTINGENCY'
Estado del comprobante al responder (READY_TO_ISSUE recién encolado; sondear GET /documents/{id}).
issue.enqueued *
boolean
true si esta llamada encoló el pipeline; false si ya estaba emitido o en curso (no-op idempotente).
GET
/documents/{id}/pdf
Descargar PDF
documents:read

Representación imprimible del comprobante, renderizada on-demand (nunca se persiste; el artefacto fiscal autoritativo es el XML firmado). Scope requerido: documents:read.

Parámetros
id *
string · path
Identificador del comprobante.
Respuesta 200 — application/pdf
GET
/documents/{id}/xml
Descargar XML firmado
documents:read

El XML firmado del comprobante — el artefacto fiscal autoritativo. Devuelve 409 CONFLICT mientras el documento exista pero aún no haya sido firmado por el pipeline. Scope requerido: documents:read.

Parámetros
id *
string · path
Identificador del comprobante.
Respuesta 200 — application/xml

Contactos

Clientes y proveedores (receptores de comprobantes).

GET
/contacts
Listar contactos
contacts:read

Listado de contactos (clientes y proveedores) de la empresa. Scope requerido: contacts:read.

Parámetros
type
'CUSTOMER' | 'SUPPLIER' | 'BOTH' · query
Filtra por tipo de contacto.
search
string · query
Búsqueda por nombre, documento o código.
includeInactive
boolean · query
Incluye contactos desactivados.
limit
integer · query
Cantidad máxima de resultados (1-100).
offset
integer · query
Desplazamiento para paginar (0-10000). Para recorrer más allá usá paginación por cursor donde esté disponible.
Respuesta 200 (application/json)
data *
object[]
Página de contactos.
data[].id
string
Id interno del contacto (identificador del recurso en /contacts/{id}). Para referenciarlo al crear documentos se usa su código (receiverCode).
data[].type
'CUSTOMER' | 'SUPPLIER' | 'BOTH'
CUSTOMER = cliente, SUPPLIER = proveedor, BOTH = ambos.
data[].code
string
Código interno único por empresa, obligatorio (usable como receiverCode al crear documentos).
data[].documentType
'NIE' | 'RUC' | 'CI' | 'PASSPORT' | 'DNI' | 'NIFE' | 'OTHER' | null
Tipo de documento fiscal: NIE (1, id extranjero UY), RUC (2), CI (3), OTHER (4), PASSPORT (5), DNI (6), NIFE (7, id fiscal extranjero).
data[].countryCode
string | null
País del documento — ISO 3166-1 alpha-2 (UY, AR, BR, …).
data[].documentNumber
string | null
Número de documento como string (puede tener ceros a la izquierda).
data[].legalName
string
Razón social o nombre completo.
data[].tradeName
string | null
Nombre comercial (opcional).
data[].email
string | null
Email de contacto (usado para el envío del CFE).
data[].phone
string | null
Teléfono de contacto.
data[].city
string | null
Ciudad.
data[].department
string | null
Departamento.
data[].isActive
boolean
false = desactivado (no aparece en listados por defecto).
data[].createdAt
string (fecha-hora)
Momento de creación (ISO 8601).
pagination *
object
Metadatos de paginación de los listados.
pagination.total
integer
Total de resultados que matchean los filtros. Presente salvo que el listado acepte includeTotal y se haya enviado false.
pagination.limit *
integer
Límite aplicado (1-100).
pagination.offset *
integer
Desplazamiento aplicado.
POST
/contacts
Crear un contacto
contacts:write

Crea un contacto con el mismo schema y validaciones que el formulario de la app: dígito verificador de RUC (módulo 11), unicidad por empresa de documento y código interno, reglas de receptor. code y legalName son requeridos. Scope requerido: contacts:write.

Parámetros
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
type *
'CUSTOMER' | 'SUPPLIER' | 'BOTH'
CUSTOMER = cliente, SUPPLIER = proveedor, BOTH = ambos.
code *
string
Código interno (único por empresa). Es el receiverCode que después usás al crear documentos.
legalName *
string
Razón social.
tradeName
string | null
Nombre comercial (opcional).
documentType
'NIE' | 'RUC' | 'CI' | 'PASSPORT' | 'DNI' | 'NIFE' | 'OTHER'
Tipo de documento fiscal: NIE, RUC (con dígito verificador módulo 11), CI, PASSPORT, DNI, NIFE, OTHER.
countryCode
string
País del documento — ISO 3166-1 alpha-2. RUC/CI/NIE exigen UY.
documentNumber
string | null
Número de documento como string (respetar ceros a la izquierda). Único por empresa junto a tipo y país.
email
string | null
Email de contacto (usado para el envío del CFE emitido).
phone
string | null
Teléfono de contacto.
address
string | null
Domicilio fiscal (Área Receptor del CFE).
city
string | null
Ciudad.
department
string | null
Departamento.
Respuesta 201 (application/json)
id
string
Id interno del contacto (identificador del recurso en /contacts/{id}). Para referenciarlo al crear documentos se usa su código (receiverCode).
type
'CUSTOMER' | 'SUPPLIER' | 'BOTH'
CUSTOMER = cliente, SUPPLIER = proveedor, BOTH = ambos.
code
string
Código interno único por empresa, obligatorio (usable como receiverCode al crear documentos).
documentType
'NIE' | 'RUC' | 'CI' | 'PASSPORT' | 'DNI' | 'NIFE' | 'OTHER' | null
Tipo de documento fiscal: NIE (1, id extranjero UY), RUC (2), CI (3), OTHER (4), PASSPORT (5), DNI (6), NIFE (7, id fiscal extranjero).
countryCode
string | null
País del documento — ISO 3166-1 alpha-2 (UY, AR, BR, …).
documentNumber
string | null
Número de documento como string (puede tener ceros a la izquierda).
legalName
string
Razón social o nombre completo.
tradeName
string | null
Nombre comercial (opcional).
email
string | null
Email de contacto (usado para el envío del CFE).
phone
string | null
Teléfono de contacto.
city
string | null
Ciudad.
department
string | null
Departamento.
isActive
boolean
false = desactivado (no aparece en listados por defecto).
createdAt
string (fecha-hora)
Momento de creación (ISO 8601).
GET
/contacts/{id}
Detalle de un contacto
contacts:read

Detalle completo del contacto, incluyendo dirección, datos de receptor y metadatos. Scope requerido: contacts:read.

Parámetros
id *
string · path
Identificador del contacto.
Respuesta 200 (application/json)
id
string
Id interno del contacto (identificador del recurso en /contacts/{id}). Para referenciarlo al crear documentos se usa su código (receiverCode).
type
'CUSTOMER' | 'SUPPLIER' | 'BOTH'
CUSTOMER = cliente, SUPPLIER = proveedor, BOTH = ambos.
code
string
Código interno único por empresa, obligatorio (usable como receiverCode al crear documentos).
documentType
'NIE' | 'RUC' | 'CI' | 'PASSPORT' | 'DNI' | 'NIFE' | 'OTHER' | null
Tipo de documento fiscal: NIE (1, id extranjero UY), RUC (2), CI (3), OTHER (4), PASSPORT (5), DNI (6), NIFE (7, id fiscal extranjero).
countryCode
string | null
País del documento — ISO 3166-1 alpha-2 (UY, AR, BR, …).
documentNumber
string | null
Número de documento como string (puede tener ceros a la izquierda).
legalName
string
Razón social o nombre completo.
tradeName
string | null
Nombre comercial (opcional).
email
string | null
Email de contacto (usado para el envío del CFE).
phone
string | null
Teléfono de contacto.
city
string | null
Ciudad.
department
string | null
Departamento.
isActive
boolean
false = desactivado (no aparece en listados por defecto).
createdAt
string (fecha-hora)
Momento de creación (ISO 8601).
PATCH
/contacts/{id}
Actualizar un contacto
contacts:write

Actualización COMPLETA (semántica PUT sobre el verbo PATCH): el body debe incluir el objeto entero — type, code y legalName son obligatorios y un campo omitido se limpia. Recomendado: GET del contacto, modificar los campos deseados y reenviar todo. Acepta además isActive para activar/desactivar. Scope requerido: contacts:write.

Parámetros
id *
string · path
Identificador del contacto.
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
legalName
string
Razón social.
tradeName
string | null
Nombre comercial.
email
string | null
Email de contacto.
phone
string | null
Teléfono de contacto.
isActive
boolean
false desactiva el contacto (deja de aparecer en listados por defecto).
Respuesta 200 (application/json)
id
string
Id interno del contacto (identificador del recurso en /contacts/{id}). Para referenciarlo al crear documentos se usa su código (receiverCode).
type
'CUSTOMER' | 'SUPPLIER' | 'BOTH'
CUSTOMER = cliente, SUPPLIER = proveedor, BOTH = ambos.
code
string
Código interno único por empresa, obligatorio (usable como receiverCode al crear documentos).
documentType
'NIE' | 'RUC' | 'CI' | 'PASSPORT' | 'DNI' | 'NIFE' | 'OTHER' | null
Tipo de documento fiscal: NIE (1, id extranjero UY), RUC (2), CI (3), OTHER (4), PASSPORT (5), DNI (6), NIFE (7, id fiscal extranjero).
countryCode
string | null
País del documento — ISO 3166-1 alpha-2 (UY, AR, BR, …).
documentNumber
string | null
Número de documento como string (puede tener ceros a la izquierda).
legalName
string
Razón social o nombre completo.
tradeName
string | null
Nombre comercial (opcional).
email
string | null
Email de contacto (usado para el envío del CFE).
phone
string | null
Teléfono de contacto.
city
string | null
Ciudad.
department
string | null
Departamento.
isActive
boolean
false = desactivado (no aparece en listados por defecto).
createdAt
string (fecha-hora)
Momento de creación (ISO 8601).

Productos

Catálogo de productos y servicios.

GET
/products
Listar productos
products:read

Listado de productos y servicios del catálogo de la empresa. Scope requerido: products:read.

Parámetros
type
'PRODUCT' | 'SERVICE' · query
Filtra por tipo.
search
string · query
Búsqueda por nombre o SKU.
includeInactive
boolean · query
Incluye productos desactivados.
limit
integer · query
Cantidad máxima de resultados (1-100).
offset
integer · query
Desplazamiento para paginar (0-10000). Para recorrer más allá usá paginación por cursor donde esté disponible.
Respuesta 200 (application/json)
data *
object[]
Página de productos.
data[].id
string
Id interno del producto (identificador del recurso en /products/{id}). Para referenciarlo en las líneas de documentos se usa su SKU (productCode).
data[].type
'PRODUCT' | 'SERVICE'
PRODUCT = bien físico (puede manejar stock); SERVICE = servicio.
data[].sku
string
SKU único por empresa (usable como productCode en las líneas de documentos).
data[].name
string
Nombre del producto o servicio.
data[].unit
string | null
Unidad de medida por defecto, p.ej. "UN", "kg", "N/A".
data[].currency
string
Moneda del precio de lista (ISO 4217).
data[].price
string
Precio de venta. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
data[].isActive
boolean
false = desactivado (no aparece en listados por defecto).
data[].createdAt
string (fecha-hora)
Momento de creación (ISO 8601).
pagination *
object
Metadatos de paginación de los listados.
pagination.total
integer
Total de resultados que matchean los filtros. Presente salvo que el listado acepte includeTotal y se haya enviado false.
pagination.limit *
integer
Límite aplicado (1-100).
pagination.offset *
integer
Desplazamiento aplicado.
POST
/products
Crear un producto
products:write

Crea un producto o servicio. sku, name y price son requeridos; los flags de stock solo aplican a type: "PRODUCT". Mismas validaciones que la app (SKU único por empresa). Scope requerido: products:write.

Parámetros
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
type *
'PRODUCT' | 'SERVICE'
PRODUCT = bien físico (habilita flags de stock); SERVICE = servicio.
sku *
string
SKU único por empresa. Es el productCode que después usás en las líneas de documentos.
name *
string
Nombre del producto o servicio.
description
string | null
Descripción interna (opcional).
unit
string | null
Unidad de medida por defecto (máx. 4 caracteres), p.ej. "UN", "kg", "N/A".
currency
string
Moneda del precio de lista (ISO 4217).
price *
string
Precio de venta. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
defaultBillingIndicator
integer
Indicador de facturación por defecto al usar el producto en una línea (1-17; p.ej. 1 exento, 2 tasa mínima, 3 tasa básica).
tracksStock
boolean
Solo para PRODUCT.
deductsStockOnSale
boolean
Solo para PRODUCT con tracksStock.
allowsNegativeStock
boolean
Solo para PRODUCT con tracksStock.
restocksOnCreditNote
boolean
Repone stock ante notas de crédito (NC) que referencian un comprobante que descontó stock. Las notas de débito (ND) no mueven stock. Solo para PRODUCT con tracksStock.
minimumStock
string
Stock mínimo (string decimal). Solo con tracksStock.
Respuesta 201 (application/json)
id
string
Id interno del producto (identificador del recurso en /products/{id}). Para referenciarlo en las líneas de documentos se usa su SKU (productCode).
type
'PRODUCT' | 'SERVICE'
PRODUCT = bien físico (puede manejar stock); SERVICE = servicio.
sku
string
SKU único por empresa (usable como productCode en las líneas de documentos).
name
string
Nombre del producto o servicio.
unit
string | null
Unidad de medida por defecto, p.ej. "UN", "kg", "N/A".
currency
string
Moneda del precio de lista (ISO 4217).
price
string
Precio de venta. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
isActive
boolean
false = desactivado (no aparece en listados por defecto).
createdAt
string (fecha-hora)
Momento de creación (ISO 8601).
GET
/products/{id}
Detalle de un producto
products:read

Detalle completo del producto, incluyendo defaults DGI y configuración de stock. Scope requerido: products:read.

Parámetros
id *
string · path
Identificador del producto.
Respuesta 200 (application/json)
id
string
Id interno del producto (identificador del recurso en /products/{id}). Para referenciarlo en las líneas de documentos se usa su SKU (productCode).
type
'PRODUCT' | 'SERVICE'
PRODUCT = bien físico (puede manejar stock); SERVICE = servicio.
sku
string
SKU único por empresa (usable como productCode en las líneas de documentos).
name
string
Nombre del producto o servicio.
unit
string | null
Unidad de medida por defecto, p.ej. "UN", "kg", "N/A".
currency
string
Moneda del precio de lista (ISO 4217).
price
string
Precio de venta. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
isActive
boolean
false = desactivado (no aparece en listados por defecto).
createdAt
string (fecha-hora)
Momento de creación (ISO 8601).
PATCH
/products/{id}
Actualizar un producto
products:write

Actualización COMPLETA (semántica PUT sobre el verbo PATCH): el body debe incluir el objeto entero — type, sku, name y price son obligatorios y un campo omitido se limpia. Recomendado: GET del producto, modificar y reenviar todo. Acepta además isActive. Scope requerido: products:write.

Parámetros
id *
string · path
Identificador del producto.
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
name
string
Nombre del producto o servicio.
price
string
Precio de venta. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
isActive
boolean
false desactiva el producto (deja de aparecer en listados por defecto).
Respuesta 200 (application/json)
id
string
Id interno del producto (identificador del recurso en /products/{id}). Para referenciarlo en las líneas de documentos se usa su SKU (productCode).
type
'PRODUCT' | 'SERVICE'
PRODUCT = bien físico (puede manejar stock); SERVICE = servicio.
sku
string
SKU único por empresa (usable como productCode en las líneas de documentos).
name
string
Nombre del producto o servicio.
unit
string | null
Unidad de medida por defecto, p.ej. "UN", "kg", "N/A".
currency
string
Moneda del precio de lista (ISO 4217).
price
string
Precio de venta. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
isActive
boolean
false = desactivado (no aparece en listados por defecto).
createdAt
string (fecha-hora)
Momento de creación (ISO 8601).

Sucursales

Sucursales (casa central y locales) de la empresa — datos de referencia para emitir documentos.

GET
/branches
Listar sucursales
documents:read

Listado de sucursales (casa central y locales) de la empresa, la de por defecto primero. Es la forma de descubrir los branchCode que se usan al crear documentos. Scope requerido: documents:read.

Parámetros
search
string · query
Búsqueda por código o nombre de sucursal.
includeInactive
boolean · query
Incluye sucursales desactivadas.
limit
integer · query
Cantidad máxima de resultados (1-100).
offset
integer · query
Desplazamiento para paginar (0-10000). Para recorrer más allá usá paginación por cursor donde esté disponible.
Respuesta 200 (application/json)
data *
object[]
Página de sucursales.
data[].code *
string
Código de sucursal (único por empresa). Es el branchCode que usás al crear documentos.
data[].name
string | null
Nombre de la sucursal; null si no tiene.
data[].isDefault *
boolean
true = sucursal por defecto (la que se usa al crear un documento sin branchCode).
data[].isActive *
boolean
false = sucursal desactivada (no aparece en el listado por defecto).
data[].createdAt *
string (fecha-hora)
Momento de creación (ISO 8601).
pagination *
object
Metadatos de paginación de los listados.
pagination.total
integer
Total de resultados que matchean los filtros. Presente salvo que el listado acepte includeTotal y se haya enviado false.
pagination.limit *
integer
Límite aplicado (1-100).
pagination.offset *
integer
Desplazamiento aplicado.

Inventario

Stock por sucursal, kardex y movimientos manuales. Requiere el módulo inventory en el plan.

GET
/inventory
Stock por producto y sucursal
inventory:read

Stock actual por producto y sucursal con estado derivado (normal, low, out, negative). Requiere el módulo inventory en el plan de la empresa (si no, 403). Scope requerido: inventory:read.

Parámetros
search
string · query
Búsqueda por producto o SKU.
branchId
string · query
Filtra por sucursal.
status
'all' | 'normal' | 'low' | 'out' | 'negative' · query
Filtra por estado de stock.
limit
integer · query
Cantidad máxima de resultados (1-100).
offset
integer · query
Desplazamiento para paginar (0-10000). Para recorrer más allá usá paginación por cursor donde esté disponible.
Respuesta 200 (application/json)
data *
object[]
Página de stock por producto y sucursal.
data[].productId
string
Id del producto.
data[].productName
string
Nombre del producto.
data[].sku
string | null
SKU del producto (código único por empresa).
data[].unit
string | null
Unidad de medida del producto.
data[].branchId
string
Id de la sucursal.
data[].branchCode
string
Código de sucursal DGI.
data[].branchName
string | null
Nombre de la sucursal.
data[].quantity
string
Stock actual. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
data[].minimumStock
string | null
Stock mínimo configurado (string decimal) o null.
data[].allowsNegativeStock
boolean
true si el producto admite stock por debajo de cero.
data[].deductsStockOnSale
boolean
true si emitir una venta descuenta stock automáticamente.
data[].status
'normal' | 'low' | 'out' | 'negative'
Estado derivado del stock: normal, low (bajo el mínimo), out (en cero), negative (por debajo de cero).
statusCounts *
object
Contadores por estado de stock.
statusCounts.all
integer
Total de filas producto×sucursal.
statusCounts.normal
integer
Con stock normal.
statusCounts.low
integer
Por debajo del stock mínimo.
statusCounts.out
integer
En cero.
statusCounts.negative
integer
Por debajo de cero.
pagination *
object
Metadatos de paginación de los listados.
pagination.total
integer
Total de resultados que matchean los filtros. Presente salvo que el listado acepte includeTotal y se haya enviado false.
pagination.limit *
integer
Límite aplicado (1-100).
pagination.offset *
integer
Desplazamiento aplicado.
GET
/inventory/movements
Kardex (movimientos de inventario)
inventory:read

Historial de movimientos de stock (kardex): manuales, ajustes y automáticos por venta/NC. Requiere el módulo inventory en el plan. Scope requerido: inventory:read.

Parámetros
productId
string · query
Filtra por producto.
branchId
string · query
Filtra por sucursal.
type
'MANUAL_IN' | 'MANUAL_OUT' | 'ADJUSTMENT' | 'SALE' | 'SALE_REVERSAL' | 'CREDIT_NOTE' | 'CREDIT_NOTE_REVERSAL' | 'TRANSFER_IN' | 'TRANSFER_OUT' · query
Filtra por tipo de movimiento.
limit
integer · query
Cantidad máxima de resultados (1-100).
offset
integer · query
Desplazamiento para paginar (0-10000). Para recorrer más allá usá paginación por cursor donde esté disponible.
Respuesta 200 (application/json)
data *
object[]
Página del kardex (movimientos más recientes primero).
data[].id
string
Id del movimiento.
data[].referenceItemId
string | null
Id de la línea del comprobante que originó el movimiento (null en movimientos manuales).
data[].type
'MANUAL_IN' | 'MANUAL_OUT' | 'ADJUSTMENT' | 'SALE' | 'SALE_REVERSAL' | 'CREDIT_NOTE' | 'CREDIT_NOTE_REVERSAL' | 'TRANSFER_IN' | 'TRANSFER_OUT'
Origen del movimiento: MANUAL_IN/MANUAL_OUT (manual), ADJUSTMENT (recuento físico), SALE/SALE_REVERSAL (venta emitida/revertida), CREDIT_NOTE/CREDIT_NOTE_REVERSAL (nota de crédito — las notas de débito no mueven stock), TRANSFER_IN/TRANSFER_OUT (transferencia entre sucursales).
data[].quantityDelta
string
Variación de stock (positiva o negativa). Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
data[].previousQuantity
string | null
Stock antes del movimiento (string decimal).
data[].newQuantity
string | null
Stock después del movimiento (string decimal).
data[].reason
string | null
Motivo declarado (movimientos manuales/ajustes).
data[].referenceType
string | null
Tipo de referencia (p.ej. documento fiscal) cuando el movimiento es automático.
data[].referenceId
string | null
Id del registro referenciado (p.ej. el documento fiscal que originó el movimiento).
data[].productId
string
Id del producto.
data[].productName
string
Nombre del producto.
data[].branchId
string
Id de la sucursal.
data[].branchCode
string
Código de sucursal DGI.
data[].createdByUserId
string | null
Id del usuario que registró el movimiento; null en movimientos automáticos.
data[].createdByUserName
string | null
Nombre del usuario que registró el movimiento.
data[].createdAt
string (fecha-hora)
Momento del movimiento (ISO 8601).
pagination *
object
Metadatos de paginación de los listados.
pagination.total
integer
Total de resultados que matchean los filtros. Presente salvo que el listado acepte includeTotal y se haya enviado false.
pagination.limit *
integer
Límite aplicado (1-100).
pagination.offset *
integer
Desplazamiento aplicado.
POST
/inventory/adjustments
Movimiento manual de stock
inventory:write

Crea un movimiento manual auditado. MANUAL_IN/MANUAL_OUT llevan la cantidad movida (quantity); ADJUSTMENT es un recuento físico: lleva countedQuantity (el delta se calcula server-side) y reason obligatorio. Mismas garantías que la app: SELECT FOR UPDATE sobre el balance, sin stock negativo salvo que el producto lo permita. Requiere el módulo inventory en el plan. Scope requerido: inventory:write.

Parámetros
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
type *
'MANUAL_IN'
Entrada manual de stock.
productId *
string
Id del producto (debe tener tracksStock).
branchId *
string
Id de la sucursal donde ingresa el stock.
quantity *
string
Cantidad que ingresa. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
reason
string
Motivo del ingreso (opcional).
type *
'MANUAL_OUT'
Salida manual de stock.
productId *
string
Id del producto (debe tener tracksStock).
branchId *
string
Id de la sucursal de la que egresa el stock.
quantity *
string
Cantidad que egresa. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
reason
string
Motivo del egreso (opcional).
type *
'ADJUSTMENT'
Ajuste por recuento físico (el delta se calcula server-side).
productId *
string
Id del producto (debe tener tracksStock).
branchId *
string
Id de la sucursal recontada.
countedQuantity *
string
Cantidad contada físicamente. Decimal serializado como string; salida con coma decimal es-UY («12200,50»), entrada acepta coma o punto.
reason *
string
Motivo del ajuste (obligatorio).
Respuesta 201 (application/json)
type *
'MANUAL_IN' | 'MANUAL_OUT' | 'ADJUSTMENT'
Tipo del movimiento registrado (eco del request).

Cobros

Cobros de clientes, cuentas por cobrar con aging y estado de cobro de comprobantes. Metadata comercial que nunca modifica el registro fiscal. Requiere el módulo «payments» habilitado en el plan de la empresa — sin él, todos estos endpoints responden 403. Scopes: payments:read / payments:write.

POST
/payments
Registrar un cobro
payments:write

Registra un cobro de cliente (total, parcial o a cuenta), opcionalmente imputado a comprobantes. contactId: null = consumidor final (exige al menos una imputación). Reglas: los documentos imputados deben ser emitidos ACCEPTED/OBSERVED, del mismo receptor y moneda, y ninguna imputación puede superar el saldo pendiente. Idempotente vía Idempotency-Key (header) o idempotencyKey (body): un reintento con la misma clave devuelve el cobro ya creado. El registro NUNCA modifica el comprobante fiscal. Scope requerido: payments:write.

Parámetros
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
contactId *
string | null
Contacto receptor, o null para consumidor final.
paidOn *
string
Fecha del cobro (YYYY-MM-DD, zona de la app).
amount *
string
Monto total del cobro (decimal como string).
currency *
string
ISO-4217 de 3 letras.
method *
'TRANSFER' | 'CASH' | 'CHECK' | 'CARD' | 'OTHER'
Medio de cobro: TRANSFER (transferencia), CASH (efectivo), CHECK (cheque), CARD (tarjeta), OTHER.
reference
string
Referencia externa del cobro (nº de transferencia, cheque, etc.).
note
string
Nota interna (opcional).
idempotencyKey
string
Idempotencia opcional (o header Idempotency-Key, que gana). Un reintento con la misma clave devuelve el cobro ya creado.
allocations *
object[]
Imputaciones a comprobantes. Vacío = pago a cuenta.
allocations[].fiscalDocumentId *
string
Id del comprobante al que se imputa.
allocations[].amount *
string
Monto imputado (string decimal, ≤ saldo pendiente del comprobante).
Respuesta 201 (application/json)
id *
string
Id del cobro registrado.
POST
/payments/{id}/allocations
Aplicar crédito de un cobro a cuenta
payments:write

Imputa el crédito disponible de un cobro a cuenta existente (monto − imputaciones activas) a comprobantes del mismo receptor y moneda. No crea un cobro nuevo ni modifica el registro fiscal. Scope requerido: payments:write.

Parámetros
id *
string · path
Identificador del cobro a cuenta.
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Body (application/json)
allocations *
object[]
Imputaciones a comprobantes del mismo receptor y moneda.
allocations[].fiscalDocumentId *
string
Id del comprobante al que se imputa.
allocations[].amount *
string
Monto imputado (string decimal, ≤ crédito disponible y ≤ saldo del comprobante).
Respuesta 200 (application/json)
applied *
string
Total imputado en esta llamada (string decimal).
remainingCredit *
string
Crédito a cuenta restante del cobro (string decimal).
DELETE
/payments/{id}
Anular un cobro
payments:write

Anulación lógica: el cobro se conserva con AuditLog y deja de contar en todos los saldos. Un cobro inexistente o ya anulado devuelve 404. Scope requerido: payments:write.

Parámetros
id *
string · path
Identificador del cobro a anular.
Idempotency-Key
string · header
Clave de idempotencia (8-128 caracteres, única por empresa y operación). Un reintento con la misma clave y el mismo body devuelve la respuesta original (header Idempotency-Replayed: true) durante 24 h; la misma clave con otro body responde 409 IDEMPOTENCY_CONFLICT; si el primer intento sigue en curso, 409 con Retry-After.
Respuesta 200 (application/json)
id *
string
Id del cobro anulado.
cancelled *
boolean
true = el cobro quedó anulado (deja de contar en saldos).
GET
/payments/receivables
Cuentas por cobrar
payments:read

Saldos pendientes de cobro de comprobantes emitidos ACCEPTED/OBSERVED, agrupados por moneda y con antigüedad (aging): a vencer / vencido 1–30 / 31–60 / +60 días / sin vencimiento. Las notas de crédito quedan excluidas. Scope requerido: payments:read.

Respuesta 200 (application/json)
truncated *
boolean
true si la lista de filas fue recortada por tamaño (consultar con filtros más finos).
blocks *
object[]
Un bloque por moneda con saldos pendientes.
blocks[].currency
string
Moneda del bloque (ISO 4217).
blocks[].totalOutstanding
string
Saldo pendiente total del bloque (string decimal).
blocks[].bucketTotals
object
Totales por antigüedad: NOT_DUE (a vencer), D1_30, D31_60, D60_PLUS (días vencidos), NO_DUE_DATE (sin vencimiento).
blocks[].bucketTotals.NOT_DUE
string
A vencer.
blocks[].bucketTotals.D1_30
string
Vencido 1-30 días.
blocks[].bucketTotals.D31_60
string
Vencido 31-60 días.
blocks[].bucketTotals.D60_PLUS
string
Vencido más de 60 días.
blocks[].bucketTotals.NO_DUE_DATE
string
Sin fecha de vencimiento.
blocks[].rows
object[]
Comprobantes con saldo pendiente, más vencidos primero.
blocks[].rows[].documentId
string
Id del comprobante.
blocks[].rows[].label
string
Etiqueta legible (tipo + serie-número).
blocks[].rows[].receiverId
string | null
Id del contacto receptor.
blocks[].rows[].receiverName
string | null
Nombre del receptor.
blocks[].rows[].issueDate
string | null
Fecha de emisión (ISO 8601).
blocks[].rows[].dueDate
string | null
Fecha de vencimiento (ISO 8601); null si no tiene.
blocks[].rows[].daysOverdue
integer
Días vencidos (0 si aún no vence).
blocks[].rows[].bucket
'NOT_DUE' | 'D1_30' | 'D31_60' | 'D60_PLUS' | 'NO_DUE_DATE'
Bucket de antigüedad del saldo.
blocks[].rows[].total
string
Total del comprobante (string decimal).
blocks[].rows[].allocated
string
Cobros imputados (string decimal).
blocks[].rows[].outstanding
string
Saldo pendiente (string decimal).
GET
/documents/{id}/collection
Estado de cobro de un comprobante
payments:read

Estado de cobro derivado de un comprobante: PENDING/PARTIAL/PAID, total, imputado, saldo y los cobros aplicados. Scope requerido: payments:read.

Parámetros
id *
string · path
Identificador del comprobante.
Respuesta 200 (application/json)
collectible
boolean
Si admite cobros: emitido, aceptado u observado por DGI, no recibo de cobranza y con la cobranza gestionada en facturar.uy.
collectionTracking
'TRACKED' | 'NOT_TRACKED'
Si la cobranza se gestiona en facturar.uy. NOT_TRACKED: cobrado o gestionado fuera (p. ej. histórico importado de otro proveedor); no es cuenta a cobrar ni admite cobros.
status *
'PENDING' | 'PARTIAL' | 'PAID'
Estado de cobro: PENDING (sin cobros), PARTIAL (cobrado en parte), PAID (saldado).
outstanding *
string
Saldo pendiente de cobro (string decimal).

Idempotencia de punta a punta

Idempotency-Key: erp-fact-2026-000123
POST /api/v1/documents
→ 201 { "reused": false }

POST /api/v1/documents  # retry, misma clave
→ 200 { "reused": true }

POST /api/v1/documents/{id}/issue
→ 202 { "enqueued": true }

POST /api/v1/documents/{id}/issue  # retry
→ 200 { "enqueued": false } ●

El header Idempotency-Key (o idempotencyKey en el body; el header gana) hace que un retry devuelva el documento ya creado. La numeración la gestiona la reserva de CAE — series/number se ignoran al crear — y el jobId de la cola deriva del documento: sin duplicados ni doble-encolado.

Errores, sin misterio

{ "error": { "code": "VALIDATION_ERROR", "message": "Parámetros inválidos", "details": { … } } }
401
UNAUTHORIZED — Key faltante, malformada, revocada o vencida — mismo mensaje en todos los casos.
403
FORBIDDEN — Scope insuficiente, módulo no incluido en el plan u operación no permitida.
404
NOT_FOUND — Recurso inexistente o de otra empresa (misma respuesta en ambos casos).
409
CONFLICT — Estado incompatible — p.ej. XML aún no firmado o documento anulado.
422
VALIDATION_ERROR — Query o body inválidos (zod), o borrador incompleto para emitir.
429
RATE_LIMITED — Límite de solicitudes superado — incluye el header Retry-After.
500
INTERNAL_ERROR — Error interno, opaco a propósito: los detalles quedan en los logs del servidor.

Límites: 120 req/min por API key y 240 req/min por IP. Toda respuesta incluye X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset; los 429 agregan Retry-After.

Tu primer CFE en una tarde.

Creá tu cuenta, generá tu API key y emití sobre la misma capa que usa la app. La referencia completa vive en esta página, en el spec OpenAPI y en la colección Postman.