Skip to content

Ventas ​

Una venta (Sale) es la operación comercial completa: qué se vendió, por cuánto y cómo se cobra. Agrupa los ingresos (el dinero que entra) y las cuentas por cobrar de una misma operación.

Venta o ingreso: cuál usar ​

IngresosVentas
Responde«cuánto dinero entró y cuándo»«qué se vendió»
Cobrosunouno o varios
Se puede facturarnosí

Si vas a facturar, registra ventas

Una factura no se corresponde uno a uno con un cobro. Una orden pagada en tres parcialidades se factura una vez, y doce meses de una suscripción son doce cobros donde el cliente espera una factura. El CFDI se emite sobre la venta, no sobre el ingreso: sin agrupar, tendrías doce facturas.

Para un cobro suelto que no se va a facturar, POST /incomes sigue siendo más directo.

Registrar una venta dispara los mismos efectos que hacerlo desde la aplicación: crea el ingreso o la cuenta por cobrar, deposita en la cuenta, descuenta inventario, mueve los contadores de unidades vendidas y dispara las automatizaciones de sale_created.

El objeto Sale ​

json
{
  "id":          "65c2a1f1234567890abcdef0",
  "object":      "sale",
  "status":      "partially_paid",
  "currency":    "mxn",
  "occurred_at": "2026-09-17T18:04:10Z",
  "description": "Paquete anual de mantenimiento",

  "customer_id":   "65a1b2c3d4e5f6789012abcd",
  "customer_name": "Laura Méndez",
  "account_id":    "65b3a1f1234567890abcdef0",
  "account_name":  "BBVA MXN",
  "category_id":   null,
  "category_name": null,

  "total_cents":      1160000,
  "total":            11600.0,
  "subtotal_cents":   1000000,
  "tax_cents":        160000,
  "tax_rate_percent": 16.0,
  "tax_breakdown":    [{ "rate_percent": 16.0, "base_cents": 1000000, "tax_cents": 160000 }],
  "retentions_cents": 0,
  "without_tax":      false,

  "payment_scheme":     "PPD",
  "paid_cents":         400000,
  "receivable_cents":   760000,
  "cobros_count":       1,
  "installments_total": 3,
  "installments_paid":  1,

  "invoiced":             false,
  "invoiced_total_cents": 0,
  "complemented_cents":   0,
  "cfdi_uuid":            null,

  "income_ids":              ["65c2a2001234567890abcdef"],
  "accounts_receivable_ids": ["65c2a2011234567890abcdef"],

  "recurring_transaction_id": null,
  "recurring_occurrence_on":  null,

  "metadata":   { "external_ref": "ord_123", "orderId": "ord_123" },
  "created_at": "2026-09-17T18:04:11Z",
  "updated_at": "2026-09-17T18:04:11Z"
}

Estado ​

statusQué significa
openNo se ha cobrado nada.
partially_paidEntró parte del dinero.
paidSaldada. Dispara sale.paid.
canceledCancelada.

Cobranza ​

Se publican los dos números y no sólo el estado, porque paid_cents y receivable_cents son lo que necesitas para decirle a tu cliente «te faltan $X» — derivarlo del estado es imposible.

Siempre se cumple total_cents = paid_cents + receivable_cents.

Facturación ​

payment_scheme lo decide Finova: PUE si la venta se liquidó en una sola exhibición, PPD si hay crédito o varios cobros. Ante el SAT no es opcional, y determina si cada cobro necesita complemento de pago.

Ver Facturas.


Listar ventas ​

GET/api/v1/salesread-sales

Filtros: status, customer_id, recurring_transaction_id, external_ref.


Obtener una venta ​

GET/api/v1/sales/:idread-sales

Incluye items.


Registrar una venta ​

POST/api/v1/salescreate-sales

Requeridos: account_id, currency, total_amount_cents, occurred_at.

Opcionales: customer_id, category_id, employee_id, project_id, description, without_tax, items, payment_terms, metadata.

Los items usan los mismos campos que en ingresos, y product_id es obligatorio en cada uno.

A crédito, category_id es obligatorio

Una venta que no se cobra al momento genera una cuenta por cobrar, y una CxC sin categoría no se puede clasificar en el estado de resultados. Al contado (cash_paid: true) no hace falta.

payment_terms: cómo se cobra ​

Es lo que decide si la venta nace como dinero en la cuenta o como cuenta por cobrar. Si lo omites, se asume contado sin pagar ({ "type": "cash" }).

json
{ "type": "cash", "cash_paid": true }
json
{ "type": "cash", "cash_paid": false, "due_on": "2026-10-17" }
json
{
  "type": "msi",
  "installments_count": 3,
  "first_due_on": "2026-10-01",
  "paid_installments_count": 1
}
json
{
  "type": "deposit",
  "deposit_mode": "percent",
  "deposit_percent": 30,
  "remainder_plan": "cash",
  "due_on": "2026-11-17"
}
json
{
  "type": "custom",
  "custom_payments": [
    { "amount_cents": 400000, "due_on": "2026-09-17", "paid": true },
    { "amount_cents": 380000, "due_on": "2026-10-17" },
    { "amount_cents": 380000, "due_on": "2026-11-17" }
  ]
}

Con type: "custom" los pagos tienen que sumar exactamentetotal_amount_cents, o la petición se rechaza con 422.

Ejemplo completo ​

bash
curl -X POST https://developers.fi-nova.com/api/v1/sales \
  -H 'Authorization: Bearer finova_sk_TU_SECRETO' \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "account_id":         "65b3a1f1234567890abcdef0",
      "customer_id":        "65a1b2c3d4e5f6789012abcd",
      "currency":           "mxn",
      "total_amount_cents": 1160000,
      "occurred_at":        "2026-09-17T18:04:10Z",
      "description":        "Paquete anual de mantenimiento",
      "items": [
        {
          "product_id":       "65b0a1f1234567890abcdef0",
          "name":             "Mantenimiento anual",
          "quantity":         1,
          "unit_price_cents": 1160000,
          "tax_percent":      16
        }
      ],
      "payment_terms": {
        "type": "msi",
        "installments_count": 3,
        "first_due_on": "2026-10-01"
      },
      "metadata": { "external_ref": "ord_123", "orderId": "ord_123" }
    }
  }'

Idempotencia ​

Manda metadata.external_ref. Si repites la petición con la misma referencia, recibes 200 con la venta que ya existía en vez de una segunda. En una operación que mueve dinero, reintentar a ciegas sin esto es contar el dinero dos veces sin forma de notarlo. Ver idempotencia.

Periodo cerrado ​

Si occurred_at cae en un mes que tu contabilidad ya cerró, recibes 422 period_locked. La llave no puede saltarse un cierre.


No hay DELETE ​

Deshacer una venta no es borrarla: es una devolución, que deja rastro contable y genera el egreso que la compensa. Borrar el agregado dejaría sus ingresos y cuentas por cobrar apuntando a un id que no resuelve.

Las devoluciones se registran desde la aplicación y emiten sale.refunded.

Hecho con cuidado por Finova.