Saltearse al contenido

Valida un CFE SIN emitirlo

POST
/v1/cfe/validar-cfe

Corre las MISMAS validaciones que POST /v1/cfe/emitir —ítems, receptor, cuadre de IVA y totales, CAE vigente, referencias de Notas, XSD de DGI— y devuelve la lista de problemas, sin emitir nada.

Sin certificado

Funciona aunque la empresa todavía no tenga certificado de facturación electrónica: el CFE se firma en memoria con una clave de prueba (el XSD de DGI exige la firma pero sólo revisa su estructura) y la respuesta trae data.advertencias[] avisando que para emitir falta el certificado. POST /v1/cfe/emitir sí lo exige y responde 409 CERTIFICADO_NO_VIGENTE.

Por qué importa

Un CFE quema su número aunque después se anule: no hay forma de deshacer una emisión. Validar antes es la única manera de que un cuerpo mal armado no cueste un número de la serie, y es lo que conviene cablear en el formulario antes del botón de emitir.

No consume numeración, no viaja a DGI y no persiste nada.

El cuerpo es idéntico al de POST /v1/cfe/emitir. La respuesta siempre es 200: data.valido dice si pasa, y data.errores[] trae los motivos cuando no. Un 200 con valido: false no es un error de la API — es el resultado de la validación. data.advertencias[] (opcional) lista lo que no invalida el comprobante pero impediría emitirlo hoy.

Alcance requerido: cfe:emitir o cfe:leer. Si la credencial no lo tiene, la respuesta es 403 API_ALCANCE_INSUFICIENTE con la lista en error.requerido. Las credenciales creadas antes del modelo de alcances (alcances: null) conservan acceso total.

x-cuenta-id
string format: uuid
Ejemplo
0199d1b0-0000-7000-8000-000000000000

Sólo para credenciales de cuenta proveedora (revendedor). UUID de la subcuenta de la cartera sobre la que se quiere operar: la cuenta efectiva del request pasa a ser esa.

Una credencial que no es de proveedor, o una subcuenta que no está en su cartera, recibe 403 API_CUENTA_FUERA_DE_CARTERA. Sin la cabecera, la cuenta es siempre la de la credencial.

x-branch-id
string format: uuid

Sucursal contra la que operar, cuando la credencial es global. Si la credencial está fijada a una sucursal y esta cabecera indica otra, la respuesta es 403 API_ACCESS_BRANCH_MISMATCH. Sin la cabecera se asume la casa central.

x-point-id
string format: uuid

Punto de emisión dentro de la sucursal resuelta. Obligatorio cuando la sucursal tiene 2 o más puntos activos (400 PUNTO_EMISION_REQUERIDO si falta); con uno solo se selecciona automáticamente.

El mismo cuerpo que POST /v1/cfe/emitir

object
clienteId
string format: uuid
cliente
object
tipoDocumento
Any of:
number
documento
string
razonSocial
string
denominacion
string
direccion
string
ciudad
string
estado
string
pais
string
email
string
items
required
Array<object>
>= 1 items
object
id
string format: uuid
name
string
quantity
required
number
price
number
billingIndex
integer
unit
string
description
string
code
string
codeType
string
kind
string
tipo
required
integer
moneda
required
string
Allowed values: UYU USD ARS BRL EUR
cambio
number
adenda
string
formaPago
Any of:
number
Allowed values: 1
fchVenc

A-C12 Fecha de vencimiento del pago (AAAA-MM-DD). Pensada para ventas a crédito (formaPago=2).

string
fchValor

A-C5.1 FchValor (AAAA-MM-DD) — fecha valor, EXCLUSIVA del e-Resguardo (182): la usan los organismos del Estado cuando la fecha en que se hace efectiva la retención difiere de la fecha contable de emisión. Debe caer en el MISMO MES que fchEmis (DGI exige que coincida el AAAAMM) y su año no puede pasar de 2050. En cualquier otro tipo devuelve 422.

string
esCobranza

Emite un CFE de Cobranza (e-Recibo, A-C20 IndCobPropia=1): solo e-Ticket (101) o e-Factura (111), con todas las líneas no facturables (billingIndex 6 o 7) y la leyenda “Cobranza” en la representación impresa. Opcionalmente, referenciaId apunta a la factura a crédito cobrada (Referencia Zona F).

boolean
referenciaId
string format: uuid
referencias
Array<object>
<= 40 items
object
referenciaId
required
string format: uuid
monto
required
number
exportacionInfo
object
clausulaVenta
required
string
modalidadVenta
required
integer
viaTransporte
required
integer
remitoInfo
object
tipoTraslado
required
integer
>= 1 <= 2
propiedadMercaderia
integer
clauVenta
string
modVenta
integer
viaTransp
integer
tipoTraslado
integer
>= 1 <= 2
propiedadMercaderia
integer
compraId

CompraID — estandar DGI para el nº de orden de compra / ID de pago del RECEPTOR. Viaja en el bloque Receptor del XML. Hasta 50 caracteres. No lo admite el e-Resguardo (182).

string
<= 50 characters
mandante

Venta por cuenta ajena (131/132/133 y 141/142/143): datos del mandante, es decir, por cuenta de quién se vende. Van en la zona K (Complemento Fiscal) del CFE; el RUC emisor de esa zona es siempre el de tu cuenta. Obligatorio en esos tipos, notas incluidas, y prohibido en el resto (422).

object
tipoDocumento
required

K-C2 TipoDocMdte — tipo de documento del mandante: 1=NIE, 2=RUC, 3=CI, 4=Otros, 5=Pasaporte, 6=DNI (Argentina, Brasil, Chile o Paraguay), 7=NIFE.

integer
>= 1 <= 7
pais
required

K-C3 Pais — código ISO 3166-1 alfa-2 del país emisor del documento (ej. UY).

string
>= 2 characters <= 2 characters
documento
required

K-C4 DocMdte — número de documento del mandante (hasta 20 caracteres). Si es RUC o CI uruguayos se valida el dígito verificador.

string
<= 20 characters
razonSocial
required

K-C5 NombreMdte — nombre o razón social del mandante (hasta 150 caracteres).

string
<= 150 characters
metadata

Metadatos personalizados de la cuenta (campos NO fiscales definidos desde el panel web, Configuración → Campos personalizados). Las claves deben coincidir con las definidas para la cuenta; valores fuera de tipo/formato son rechazados con 422 METADATA_INVALIDA. Cada valor es un texto, un número, un booleano, una lista de textos o null (que borra el valor del campo). No soportado en POST /v1/cfe/emitir-xml.

object
key
additional properties
Any of:
string
retenciones
Array<object>
>= 1 items <= 5 items
object
codigo
required

B-C20 CodRet — código de la tabla de Retención/Percepción de DGI, formato FFFF-LLL (ej. “2183-010”). Consultable en GET /cfe/retenciones. Los códigos del formulario 2181 son CRÉDITOS FISCALES (A-C127): su importe suma a mntTotCredFisc (A-C125.1), NO a mntTotRetenido (A-C125).

string
tasa

B-C21 Tasa (%) aplicada. Opcional.

number
<= 100
monto
required

B-C22 MntSujetoaRet — base imponible sobre la que se retiene. Debe ser mayor a 0.

number
valor
required

B-C23 ValRetPerc — importe retenido/percibido.

number
infoAdicional

B-C22.1 InfoAdicionalRet — leyenda normativa de la línea (hasta 150 caracteres). Cuando se envía, la representación impresa del e-Resguardo la imprime debajo del concepto: DGI lo exige (Formato CFE v25).

string
<= 150 characters
lineasResguardo
Array<object>
>= 1 items <= 200 items
object
ajuste

Marca la línea como AJUSTE (B-C4 IndFact=9): sus importes RESTAN del agregado por código (A-C128) y por lo tanto de mntTotRetenido/mntTotCredFisc. Exige referenciaId apuntando al e-Resguardo que se ajusta.

boolean
retenciones
required

Tabla de Retención/Percepción de esta línea de detalle (Item_Resg admite hasta 5).

Array<object>
>= 1 items <= 5 items
object
codigo
required

B-C20 CodRet — código de la tabla de Retención/Percepción de DGI, formato FFFF-LLL (ej. “2183-010”). Consultable en GET /cfe/retenciones. Los códigos del formulario 2181 son CRÉDITOS FISCALES (A-C127): su importe suma a mntTotCredFisc (A-C125.1), NO a mntTotRetenido (A-C125).

string
tasa

B-C21 Tasa (%) aplicada. Opcional.

number
<= 100
monto
required

B-C22 MntSujetoaRet — base imponible sobre la que se retiene. Debe ser mayor a 0.

number
valor
required

B-C23 ValRetPerc — importe retenido/percibido.

number
infoAdicional

B-C22.1 InfoAdicionalRet — leyenda normativa de la línea (hasta 150 caracteres). Cuando se envía, la representación impresa del e-Resguardo la imprime debajo del concepto: DGI lo exige (Formato CFE v25).

string
<= 150 characters
mntTotRetenido

A-C125 MntTotRetenido — total RETENIDO del e-Resguardo. Solo los códigos que NO son del formulario 2181: los 2181xxx son créditos fiscales y van a A-C125.1 mntTotCredFisc. Si se omite, se deriva de la suma (con signo: las líneas de ajuste restan) de los importes no-2181; si se envía, debe coincidir con esa suma. Puede ser 0 (resguardo que solo documenta créditos fiscales) o negativo (resguardo de ajuste).

number
Ejemplos

e-Ticket a consumidor final (sin datos de receptor)

Caso típico de POS: venta de bajo monto a consumidor final, sin RUT ni datos del cliente. Para tipo 101 (e-Ticket), si el monto neto en UI no supera el tope (~10.000 UI), el receptor es opcional.

{
"tipo": 101,
"moneda": "UYU",
"formaPago": 1,
"items": [
{
"name": "Café americano",
"quantity": 2,
"price": 90,
"billingIndex": 3
},
{
"name": "Medialuna",
"quantity": 1,
"price": 60,
"billingIndex": 3
}
]
}

Resultado de la validación. Mirá data.valido: el 200 significa que la validación CORRIÓ, no que el CFE sea válido.

object
Ejemplos
{
"data": {
"valido": true,
"errores": []
}
}

El cuerpo no parsea contra el esquema. Es un error de FORMA, distinto de un CFE bien formado pero no emitible, que sale 200 con valido: false.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Cédula uruguaya con dígito verificador inválido

{
"error": {
"code": "CLIENT_CI_INVALID",
"message": "La cédula uruguaya del cliente no es válida (dígito verificador incorrecto)"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

No autenticado. La API_KEY no fue enviada, no es válida o la credencial está desactivada.

Códigos posibles: API_AUTH_HEADER_MISSING, API_AUTH_HEADER_INVALID, API_ACCESS_INVALID.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Falta header Authorization

{
"error": {
"code": "API_AUTH_HEADER_MISSING",
"message": "Se esperaba la cabecera Authorization con esquema Bearer"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

Acceso prohibido. La credencial es válida pero algo del contexto lo impide. Las cinco causas se corrigen de forma distinta y ninguna se arregla reintentando:

CodeQué pasóCómo se corrige
API_FEATURE_DISABLEDLa cuenta no tiene la API habilitada en su planPedir la habilitación a soporte o al proveedor
API_ALCANCE_INSUFICIENTELa credencial no tiene el alcance de la operación (va en error.requerido)Editar la credencial en Integraciones → API
API_ALCANCE_NO_DISPONIBLELa credencial tiene el alcance pero la cuenta no lo admite: sin cobros habilitados (pagos:*), sin facturación (cfe:*) o no es cuenta proveedora (webhooks:gestionar). Va en error.detailsPedir la habilitación a soporte o al proveedor
API_ACCESS_BRANCH_MISMATCHx-branch-id distinto de la sucursal fijada en la credencialQuitar la cabecera o usar otra credencial
API_CUENTA_FUERA_DE_CARTERAx-cuenta-id apunta a una cuenta que no es de la carteraRevisar el UUID de la subcuenta
object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Falta el alcance

{
"error": {
"code": "API_ALCANCE_INSUFICIENTE",
"message": "La credencial de API no tiene ninguno de los alcances necesarios para esta operación (pagos:cobrar)",
"requerido": [
"pagos:cobrar"
]
},
"requestId": "3f6b1c2e-9a4d-4f80-bc11-7e2d5a8f0c31"
}

Rate limit excedido. Esperá los segundos del header Retry-After. La cuota va por credencial: 60 req/min en cuentas estándar, 600 en cuentas proveedor.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Excediste el rate limit

{
"error": {
"code": "API_RATE_LIMITED",
"message": "Se superó el límite de requests para esta API key"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}
Retry-After
integer
Ejemplo
42

Segundos hasta que se libera la ventana de rate limit