Ir al contenido

Referencia · Ventas

Crear una venta

POST/ventaCrear

Emite un Comprobante Fiscal Electrónico (CFE).

La respuesta incluye tres opciones para imprimir o mostrar el comprobante:

  • urlA4: comprobante A4 en PDF.

  • urlTicket: ticket térmico en PDF (formato estrecho).

  • htmlTicket: ticket en HTML (útil para preview o impresión web).

Cómo identifica Fixed a un cliente ya existente (para actualizarlo) en vez de crear uno nuevo, dentro de la misma empresa y mismo tipoDocumento:

  1. Si documento no viene vacío: busca por documento. Si encuentra, actualiza ese cliente. Si no encuentra ningún cliente con ese documento, busca por mailEnvioFactura (ver punto 2); si tampoco encuentra por mail, crea un cliente nuevo con ese documento.

  2. Si documento viene vacío pero mailEnvioFactura no: busca por mailEnvioFactura (sin distinguir mayúsculas/minúsculas ni espacios). Si encuentra, actualiza ese cliente. Si no encuentra, crea uno nuevo.

  3. Si tanto documento como mailEnvioFactura vienen vacíos: siempre crea un cliente nuevo.

Esto permite, por ejemplo, facturar a nombre de clientes de e-commerce que la primera vez no informan documento (solo mail), y que si en una venta posterior sí lo informan, Fixed complete el documento en la misma ficha de cliente en vez de duplicarla.

Estos campos tienen un largo máximo; si se supera, la request se rechaza con un error (no se trunca en silencio):

  • cliente.razonSocial: 150 caracteres (E18).

  • cliente.nombreComercial: 150 caracteres (E18).

  • lineasVenta[].descripcion: 80 caracteres (E24).

  • lineasVenta[].descripcionLarga: 500 caracteres (E24).

Si no recibiste la respuesta de un llamado anterior (por ejemplo por un corte de red) y no sabés si la venta se llegó a emitir, no reintentes a ciegas: mandá el campo idempotencyKey la primera vez que intentás emitir esa venta, y reusá exactamente el mismo valor, con exactamente los mismos datos, para reintentar esa misma venta. Fixed te devuelve la misma respuesta de la primera vez (sea éxito o error) en vez de reprocesar.

Una idempotencyKey queda asociada para siempre a los datos de esa venta puntual: no se puede reusar para una venta distinta, ni para reintentar la misma venta con datos corregidos. Si mandás la misma idempotencyKey con datos diferentes a los originales, Fixed devuelve el error E23. Para emitir una venta distinta, o la misma venta con datos corregidos, generá una idempotencyKey nueva.

Cómo generar la idempotencyKey: tiene que identificar el intento de emisión en sí, nunca derivarse del contenido de la venta. Si la generás a partir de los datos de la venta (por ejemplo un hash de sus campos), dos ventas distintas que coincidan en todos sus datos (mismo cliente, mismo importe, etc.) van a chocar contra la misma key, y la segunda no se va a emitir: va a recibir de vuelta el resultado de la primera.

  • Recomendado: un UUID v4 generado justo antes de intentar emitir esa venta puntual.

  • También válido: el identificador único de esa venta en tu sistema, si ya tenés uno. Puede ser el de la orden, el trabajo o el servicio.

idempotencyKey es opcional: si no lo mandás, el comportamiento es el de siempre, sin protección contra reintentos duplicados.

La guía correspondiente está en Emitir un CFE.

La ruta POST /ventaCrear se agrega a la URL base del ambiente.

Enviá los headers x-api-key y x-api-secret. Cómo obtenerlos está en Credenciales.

El cuerpo es JSON (Content-Type: application/json).

Si falta un campo obligatorio, la API rechaza la solicitud.

Campo Tipo Obligatorio Descripción Ejemplo
cliente objeto | null Sí Ver cliente. —
fecha string Sí Fecha del comprobante en formato dd/mm/aaaa. 23/06/2026
moneda string Sí Código ISO de la moneda del comprobante (UYU, USD, EUR, etc.). UYU
montoBruto number Sí 0=precios sin IVA (neto), 1=precios con IVA incluido. 1
lineasVenta array de objetos Sí Ver lineasVenta. —
observaciones string Sí Observaciones del comprobante. ""
totalAPagar number Sí Total a pagar calculado por el integrador; debe coincidir con el calculado por Fixed. 120
redondear boolean Sí Si se aplica redondeo al total. false
local string No Id de local (opcional). Consultar /localesListado. —
formaDePago string No Forma de pago (opcional, default “contado”). Valores: contado, credito. contado
cotizacion number No Cotización a usar cuando moneda es distinta de “UYU” (opcional): cuántos pesos uruguayos vale una unidad de la moneda del comprobante (por ejemplo, 43.5 si 1 EUR = $ 43,50). Si no se envía, se usa la cotización del día cargada en la empresa en Fixed o, si no hay, la del BCU. Se ignora si moneda es “UYU”. —
ordenCompra string No Número de orden de compra (opcional). ""
idempotencyKey string No Clave de idempotencia (opcional). Si reintentás un llamado a /ventaCrear (por ejemplo porque no recibiste la respuesta anterior por un corte de red) usando la misma idempotencyKey y exactamente los mismos datos, Fixed devuelve el mismo resultado de la primera vez (éxito o error) en vez de reprocesar. Reusarla con datos distintos devuelve el error E23. Ver sección “Reintentos seguros (idempotencia)” en la descripción de POST /ventaCrear, especialmente cómo generar este valor correctamente. a1b2c3d4-e5f6-4a1b-9c2d-1234567890ab
Campo Tipo Obligatorio Descripción Ejemplo
tipoDocumento number Sí Tipo de documento: 2=RUT, 3=CI, 4=Otros, 5=Pasaporte, 6=DNI, 7=NIFE. 2
documento string Sí Número de documento (RUT, CI, etc.). Obligatorio para tipoDocumento 2 (RUT); opcional para el resto. Ver sección “Identificación del cliente” en la descripción de POST /ventaCrear. 219314970014
pais string No Código ISO de país (2 letras). Obligatorio para tipoDocumento 4 (Otros), 5 (Pasaporte), 6 (DNI) y 7 (NIFE). Para tipoDocumento 6 (DNI) solo se admite AR, BR, CL o PY. AR
razonSocial string Sí Razón social del cliente. Panadería El Buen Pan S.A.
nombreComercial string Sí Nombre comercial. El Buen Pan
domicilio string Sí Domicilio fiscal. Av. Italia 1234
localidad string Sí Localidad. Montevideo
departamento string Sí Departamento. Montevideo
domicilioEntrega string Sí Domicilio de entrega. Av. Italia 1234
mailEnvioFactura string Sí Email para envío de factura. Si no se envía documento, se usa para identificar al cliente (ver sección “Identificación del cliente” en la descripción de POST /ventaCrear). facturacion@ejemplo.com
telefono string Sí Teléfono de contacto. 099123456
observaciones string Sí Observaciones del cliente. ""

Líneas del comprobante (al menos una).

Campo Tipo Obligatorio Descripción Ejemplo
cantidad number Sí Cantidad de unidades vendidas. 1
descripcion string Sí Nombre corto del ítem en el comprobante. Pan francés
descripcionLarga string Sí Detalle adicional de la línea. Pan francés artesanal
rubro string Sí Nombre o id del rubro de servicios; se crea si no existe. Panadería
tasaIva string Sí Valor cfeDgi de la tasa de IVA configurada en Fixed. Debe ser exactamente uno de los valores del enum CfeDgiTasaIva. Valores: Basic, Exp, Min, Exe, Otro, NoFac, Suspenso, Percibido, literalE. Basic
descuentoMonto number Sí Monto de descuento en la línea (no porcentaje). 0
precioUnitario number Sí Precio unitario según montoBruto. 120
codigo string No Código del ítem (opcional), por ejemplo código de barras o código interno. Se guarda bajo el primer tipo de código que la empresa tenga configurado para impresión en Fixed (o “Código” si no tiene ninguno configurado), y así viaja en el comprobante fiscal igual que si se hubiera cargado a mano. 7791234567890

CFE emitido. Respuesta incluye urlA4, urlTicket y htmlTicket

Si la operación sale bien, codigoError viene vacío.

Si el CFE ya se emitió y después falla el armado de las URLs, la respuesta sigue siendo HTTP 200, con codigoError vacío y las URLs vacías. La descripcion indica el idCFE para consultar o imprimir. No reintentes la emisión: crearías otro comprobante.

Campo Tipo Obligatorio Descripción Ejemplo
descripcion string Sí Texto libre. Vacío cuando la operación sale bien. —
codigoError string Sí Vacío cuando la operación sale bien. Con valor, es un código de error de esta operación. —
idCFE string Sí Identificador del CFE en Fixed. Guardalo: lo piden la consulta, la impresión y la devolución. —
numero number Sí Número del comprobante dentro de la serie. —
serie string Sí Serie del comprobante. —
tipoCFE number | string Sí Código numérico del comprobante según DGI. Una venta sin RUT es 101 (e-Ticket) y con RUT es 111 (e-Factura). La nota de crédito correspondiente es 102 o 112. En un error puede venir como texto vacío. —
comprobante string Sí Texto legible armado como «tipo serie número». Por ejemplo, e-Ticket A 1523. —
urlA4 string Sí URL del PDF del comprobante en formato A4. —
urlVerificarCFE string Sí Página para verificar el comprobante. En testing empieza con https://testing.fixed.uy/verificarCFE/ y en producción con https://acceso.fixed.uy/verificarCFE/. —
idVenta string No Identificador de la venta en Fixed. —
urlTicket string No URL del ticket térmico en PDF. Formato estrecho para impresoras de ticket. —
htmlTicket string No Ticket en HTML. Útil para preview en pantalla o impresión desde el navegador. —
pdfBase64 string No PDF del comprobante en formato A4, en base64. —
xml string No XML del CFE firmado. —
observaciones string No Adenda de la venta. Es el mismo valor enviado en observaciones. —

Una respuesta de error trae codigoError y descripcion. El mismo código puede significar cosas distintas según el endpoint. El índice completo está en Códigos de error.

Si fallan las credenciales o el formato del body, aplican los códigos comunes: A01 a A06, V01 y V02.

Código Significado
E02 Moneda incorrecta
E03 Empresa no encontrada
E04 Sin firma electrónica vigente
E05 Sin CAE disponible
E08 Formato de fecha inválido, debe ser dd/mm/aaaa
E15 totalAPagar no coincide con el total calculado (ver “Cómo calcula Fixed el total” en POST /ventaCrear)
E18 Datos de cliente inválidos para el tipo de documento indicado
E19 Venta > 5000 UI sin documento, domicilio, localidad o departamento del cliente (DGI los exige)
E20 formaDePago inválida
E21 cotizacion inválida (debe ser mayor a 0)
E22 Ya hay una emisión en curso con esa idempotencyKey
E23 Esa idempotencyKey ya fue usada con datos distintos
E24 descripcion supera 80 caracteres o descripcionLarga supera 500
E25 observaciones supera los 500 caracteres
E26 cantidad (mayor a 0, hasta 3 decimales), precioUnitario (mayor a 0, hasta 2 decimales) o descuentoMonto (0 o más, hasta 2 decimales) inválido en una línea
X01 Local inválido
X02 Tasa de IVA no habilitada en la empresa, o descuento mayor al importe de la línea
X03 Error inesperado generando la factura
X04 Error inesperado
X05 Error inesperado generando la venta

Error de validación o negocio (ver codigoError)

{
"descripcion": "Moneda incorrecta",
"codigoError": "E02",
"idCFE": "",
"numero": 0,
"serie": "",
"tipoCFE": "",
"comprobante": "",
"urlA4": "",
"urlVerificarCFE": ""
}

Sin cliente identificado. Una línea en UYU con IVA incluido en precio.

{
"cliente": null,
"fecha": "23/06/2026",
"moneda": "UYU",
"montoBruto": 1,
"lineasVenta": [
{
"cantidad": 1,
"descripcion": "Pan francés",
"descripcionLarga": "Pan francés artesanal",
"rubro": "Panadería",
"tasaIva": "Basic",
"descuentoMonto": 0,
"precioUnitario": 120
}
],
"observaciones": "",
"totalAPagar": 120,
"redondear": false
}

Caso típico en POS/e-commerce: varias líneas con distintos rubros y tasas de IVA. Ajustá tasaIva según lo configurado en Fixed (Basic=22%, Min=10%, Exe=0%).

{
"cliente": null,
"fecha": "23/06/2026",
"moneda": "UYU",
"montoBruto": 1,
"lineasVenta": [
{
"cantidad": 2,
"descripcion": "Pan francés",
"descripcionLarga": "Pan francés artesanal",
"rubro": "Panadería",
"tasaIva": "Basic",
"descuentoMonto": 0,
"precioUnitario": 120
},
{
"cantidad": 1,
"descripcion": "Leche entera",
"descripcionLarga": "Leche entera 1 litro",
"rubro": "Lácteos",
"tasaIva": "Min",
"descuentoMonto": 0,
"precioUnitario": 55
},
{
"cantidad": 3,
"descripcion": "Bolsa reciclable",
"descripcionLarga": "Bolsa biodegradable",
"rubro": "Accesorios",
"tasaIva": "Exe",
"descuentoMonto": 0,
"precioUnitario": 10
}
],
"observaciones": "",
"totalAPagar": 325,
"redondear": false
}

Cliente con RUT. Si ya existe, se actualizan sus datos.

{
"cliente": {
"tipoDocumento": 2,
"documento": "219314970014",
"razonSocial": "Panadería El Buen Pan S.A.",
"nombreComercial": "El Buen Pan",
"domicilio": "Av. Italia 1234",
"localidad": "Montevideo",
"departamento": "Montevideo",
"domicilioEntrega": "Av. Italia 1234",
"mailEnvioFactura": "facturacion@ejemplo.com",
"telefono": "099123456",
"observaciones": ""
},
"fecha": "23/06/2026",
"moneda": "UYU",
"montoBruto": 1,
"lineasVenta": [
{
"cantidad": 2,
"descripcion": "Docena de medialunas",
"descripcionLarga": "Medialunas de manteca",
"rubro": "Panadería",
"tasaIva": "Basic",
"descuentoMonto": 0,
"precioUnitario": 450
}
],
"observaciones": "Mostrador",
"totalAPagar": 900,
"redondear": false,
"local": ""
}

Comprobante en USD. Verificar cotización vigente de la empresa.

{
"cliente": null,
"fecha": "23/06/2026",
"moneda": "USD",
"montoBruto": 1,
"lineasVenta": [
{
"cantidad": 1,
"descripcion": "Servicio exportación",
"descripcionLarga": "",
"rubro": "Servicios",
"tasaIva": "Exp",
"descuentoMonto": 0,
"precioUnitario": 50
}
],
"observaciones": "",
"totalAPagar": 50,
"redondear": false
}

Igual a una venta simple, pero con formaDePago: “credito” en vez del default “contado”.

{
"cliente": null,
"fecha": "23/06/2026",
"moneda": "UYU",
"montoBruto": 1,
"lineasVenta": [
{
"cantidad": 1,
"descripcion": "Pan francés",
"descripcionLarga": "Pan francés artesanal",
"rubro": "Panadería",
"tasaIva": "Basic",
"descuentoMonto": 0,
"precioUnitario": 120
}
],
"observaciones": "",
"totalAPagar": 120,
"redondear": false,
"formaDePago": "credito"
}

descuentoMonto es un importe (no un porcentaje): se resta del precioUnitario x cantidad de esa línea antes de calcular el IVA.

{
"cliente": null,
"fecha": "23/06/2026",
"moneda": "UYU",
"montoBruto": 1,
"lineasVenta": [
{
"cantidad": 2,
"descripcion": "Docena de medialunas",
"descripcionLarga": "Medialunas de manteca",
"rubro": "Panadería",
"tasaIva": "Basic",
"descuentoMonto": 100,
"precioUnitario": 450
}
],
"observaciones": "Descuento por pronto pago",
"totalAPagar": 800,
"redondear": false
}

Esta forma sale del armado de la respuesta en el código. Los identificadores, el número y el tramo final de cada URL los genera Fixed al emitir. En testing el dominio es testing.fixed.uy; en producción, acceso.fixed.uy.

{
"descripcion": "",
"codigoError": "",
"idCFE": "id-generado-por-fixed",
"idVenta": "id-de-la-venta",
"numero": 1523,
"serie": "A",
"tipoCFE": 101,
"comprobante": "e-Ticket A 1523",
"urlA4": "https://testing.fixed.uy/verificarCFEverPDF/...",
"urlTicket": "https://testing.fixed.uy/verificarCFEverTicket/...",
"urlVerificarCFE": "https://testing.fixed.uy/verificarCFE/...",
"htmlTicket": "<html>...</html>",
"pdfBase64": "...",
"xml": "<?xml version=\"1.0\" ...>",
"observaciones": ""
}