# Crear una cotización

`POST https://api.fiscalisto.com/v1/quotes`

Una cotización en borrador, con los conceptos y las condiciones de pago de una factura (se revisan con las mismas reglas, así que siempre se puede facturar). customer es un cliente de su catálogo, uno con sus datos fiscales (se agrega a sus clientes) o solo name y email si aún no los tiene. Se manda con POST /v1/quotes/{id}/send.

## Parámetros

| Nombre | Dónde | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `Idempotency-Key` | encabezado |  | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). |

## Cuerpo

| Campo | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `issuer` | string |  | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. |
| `customer` | string (cus_…) o objeto | sí | Un cliente de su catálogo (cus_…), uno con sus datos fiscales (como en POST /v1/invoices: se agrega a sus clientes) o solo name y email si aún no los tiene. |
| `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. |
| `customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) |
| `customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… |
| `customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. |
| `customer.email` | string |  | Su correo (hasta 5, separados por comas). (hasta 500 caracteres) |
| `customer.foreign` | objeto |  | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. |
| `customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) |
| `customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) |
| `customer.ieps_taxpayer` | boolean |  | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. |
| `customer.name` | string | sí | A quién va dirigida. (hasta 200 caracteres) |
| `items` | arreglo de objeto | sí | Los conceptos, como en POST /v1/invoices. (hasta 200) |
| `items[].product` | string (prd_…) |  | Un producto de su catálogo (prd_…): toma su clave, unidad, precio e impuestos. |
| `items[].description` | string |  | Descripción del concepto (Descripcion), hasta 1,000 caracteres. (hasta 1000 caracteres) |
| `items[].product_key` | string |  | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… |
| `items[].unit_key` | string |  | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… (hasta 5 caracteres) |
| `items[].unit_name` | string o null |  | Nombre de la unidad (Unidad), hasta 20 caracteres. |
| `items[].sku` | string o null |  | Número de identificación o SKU (NoIdentificacion). |
| `items[].quantity` | number |  | Cantidad (Cantidad). Por omisión 1. (de -∞ a 1000000000) |
| `items[].unit_price` | number |  | Precio unitario antes de impuestos (ValorUnitario). (de 0 a 1000000000000) |
| `items[].discount` | number |  | Descuento (Descuento). (de 0 a 1000000000000) |
| `items[].taxability` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` |  | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… |
| `items[].iva` | `0`, `0.16`, `0.08`, `exento` |  | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. |
| `items[].ieps` | number |  | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. (de 0 a 72.1605) |
| `items[].ieps_factor` | `Tasa`, `Cuota` |  | Tasa o Cuota (TipoFactor del IEPS). |
| `items[].ieps_units` | number |  | Unidades de IEPS a cuota (litros, cigarros…), si no son la cantidad. (de -∞ a 1000000000) |
| `items[].iva_retention` | number |  | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). (de 0 a 0.16) |
| `items[].isr_retention` | number |  | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). (de 0 a 0.35) |
| `items[].predial` | arreglo de string |  | Números de cuenta predial (CuentaPredial) en arrendamiento. (hasta 10) |
| `items[].pedimentos` | arreglo de string |  | Números de pedimento (InformacionAduanera) de mercancía importada. (hasta 50) |
| `items[].third_party` | objeto |  | A cuenta de terceros (ACuentaTerceros): RFC, nombre, régimen y código postal. |
| `items[].third_party.tax_id` | string | sí | RFC de a cuenta de quién (RfcACuentaTerceros). (hasta 13 caracteres) |
| `items[].third_party.name` | string | sí | Nombre (NombreACuentaTerceros). (hasta 300 caracteres) |
| `items[].third_party.tax_system` | string | sí | Régimen fiscal (RegimenFiscalACuentaTerceros), c_RegimenFiscal. |
| `items[].third_party.zip` | string | sí | Código postal de su domicilio fiscal (DomicilioFiscalACuentaTerceros). |
| `items[].iedu` | objeto |  | Complemento de instituciones educativas privadas (IEDU): alumno, CURP, nivel y RVOE. |
| `items[].iedu.student_name` | string | sí | Nombre del alumno (nombreAlumno). (hasta 300 caracteres) |
| `items[].iedu.curp` | string | sí | CURP, 18 caracteres. (18 caracteres) |
| `items[].iedu.level` | `Preescolar`, `Primaria`, `Secundaria`, `Profesional técnico`, `Bachillerato o su equivalente` | sí | Nivel educativo (nivelEducativo). |
| `items[].iedu.rvoe` | string | sí | Reconocimiento de validez oficial de estudios (autRVOE). (hasta 30 caracteres) |
| `items[].iedu.payer_tax_id` | string o null |  | RFC de quien paga, si no es el receptor (rfcPago). |
| `items[].used_vehicle` | objeto |  | Complemento de vehículo usado. |
| `items[].used_vehicle.purchase_amount` | number | sí | Monto de adquisición (montoAdquisicion). (de -∞ a 1000000000000) |
| `items[].used_vehicle.sale_amount` | number | sí | Monto de enajenación (montoEnajenacion). (de -∞ a 1000000000000) |
| `items[].used_vehicle.vehicle_key` | string | sí | Clave vehicular (claveVehicular). (hasta 7 caracteres) |
| `items[].used_vehicle.make` | string | sí | Marca (marca). (hasta 50 caracteres) |
| `items[].used_vehicle.type` | string | sí | Tipo o versión (tipo). (hasta 50 caracteres) |
| `items[].used_vehicle.model` | string | sí | Año del modelo (modelo), 4 dígitos. |
| `items[].used_vehicle.engine_number` | string |  | Número de motor (numeroMotor). (hasta 17 caracteres) |
| `items[].used_vehicle.serial_number` | string |  | Número de serie (numeroSerie). (hasta 17 caracteres) |
| `items[].used_vehicle.niv` | string |  | Número de identificación vehicular (NIV). (hasta 17 caracteres) |
| `items[].used_vehicle.guide_value` | number | sí | Valor según la guía EBC o Libro Azul (valor). (de -∞ a 1000000000000) |
| `items[].used_vehicle.customs` | arreglo de objeto |  | Información aduanera de la importación del vehículo (InformacionAduanera). (hasta 10) |
| `items[].used_vehicle.customs[].number` | string | sí | Número del documento de importación (numero). (hasta 100 caracteres) |
| `items[].used_vehicle.customs[].date` | string | sí | Fecha del documento (fecha), AAAA-MM-DD. |
| `items[].used_vehicle.customs[].office` | string |  | Aduana (aduana). (hasta 100 caracteres) |
| `items[].addenda` | objeto |  | Addenda del receptor (Walmart, Soriana…), con sus propios campos. |
| `currency` | string |  | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) |
| `exchange_rate` | number |  | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) |
| `payment_form` | string |  | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. |
| `payment_method` | `PUE`, `PPD` |  | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). |
| `use` | string |  | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. |
| `notes` | string |  | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) |
| `valid_until` | string |  | Hasta cuándo es válida (AAAA-MM-DD, hasta un año). Por omisión, en 15 días. |
| `terms` | string |  | Condiciones: tiempo de entrega, anticipo, garantía… (hasta 2000 caracteres) |
| `auto_invoice` | boolean |  | Al aceptarla, timbrar la factura sola (con los datos fiscales del cliente; si no los tiene, la página se los pide primero). Por omisión false: la factura se timbra con POST /v1/quotes/{id}/invoice. |

Ejemplo:

```json
{
  "customer": "cus_OufpKqnrvx9p42",
  "payment_form": "03",
  "items": [
    {
      "description": "Desarrollo del sitio web",
      "product_key": "81112103",
      "unit_price": 45000
    }
  ],
  "terms": "50% de anticipo, entrega en 4 semanas.",
  "auto_invoice": true
}
```

## Con el SDK de TypeScript

```ts
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

const quote = await fiscalisto.quotes.create({
  customer: "cus_OufpKqnrvx9p42",
  payment_form: "03",
  items: [
    {
      description: "Desarrollo del sitio web",
      product_key: "81112103",
      unit_price: 45000,
    },
  ],
  terms: "50% de anticipo, entrega en 4 semanas.",
  auto_invoice: true,
}, { idempotencyKey: "pedido-A-1042" });
```

## Con el SDK de Python

```python
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

quote = fiscalisto.quotes.create({
    "customer": "cus_OufpKqnrvx9p42",
    "payment_form": "03",
    "items": [
        {
            "description": "Desarrollo del sitio web",
            "product_key": "81112103",
            "unit_price": 45000,
        },
    ],
    "terms": "50% de anticipo, entrega en 4 semanas.",
    "auto_invoice": True,
}, idempotency_key="pedido-A-1042")
```

## Con el SDK de PHP

```php
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

$quote = $fiscalisto->quotes->create([
    'customer' => 'cus_OufpKqnrvx9p42',
    'payment_form' => '03',
    'items' => [
        [
            'description' => 'Desarrollo del sitio web',
            'product_key' => '81112103',
            'unit_price' => 45000,
        ],
    ],
    'terms' => '50% de anticipo, entrega en 4 semanas.',
    'auto_invoice' => true,
], ['idempotency_key' => 'pedido-A-1042']);
```

## Con el SDK de .NET

```csharp
using System.Text.Json;
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var quote = await fiscalisto.Quotes.CreateAsync(new CreateQuoteParams
{
    Customer = JsonDocument.Parse("\"cus_OufpKqnrvx9p42\"").RootElement,
    PaymentForm = "03",
    Items = new()
    {
        new()
        {
            Description = "Desarrollo del sitio web",
            ProductKey = "81112103",
            UnitPrice = 45000,
        },
    },
    Terms = "50% de anticipo, entrega en 4 semanas.",
    AutoInvoice = true,
}, new RequestOptions { IdempotencyKey = "pedido-A-1042" });
```

## Con el SDK de Go

```go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	quote, err := client.Quotes.Create(ctx, &fiscalisto.CreateQuoteParams{
		Customer:    json.RawMessage(`"cus_OufpKqnrvx9p42"`),
		PaymentForm: fiscalisto.String("03"),
		Items: []fiscalisto.CreateQuoteParamsItem{
			{
				Description: fiscalisto.String("Desarrollo del sitio web"),
				ProductKey:  fiscalisto.String("81112103"),
				UnitPrice:   fiscalisto.Float64(45000),
			},
		},
		Terms:       fiscalisto.String("50% de anticipo, entrega en 4 semanas."),
		AutoInvoice: fiscalisto.Bool(true),
	}, fiscalisto.WithIdempotencyKey("pedido-A-1042"))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(quote.ID)
}
```

## Con el SDK de Java

```java
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.RequestOptions;
import com.fiscalisto.model.CreateQuoteParams;
import com.fiscalisto.model.CreateQuoteParamsItem;
import java.util.List;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var quote = fiscalisto.quotes().create(new CreateQuoteParams()
        .setCustomer("cus_OufpKqnrvx9p42")
        .setPaymentForm("03")
        .setItems(List.of(
                new CreateQuoteParamsItem()
                        .setDescription("Desarrollo del sitio web")
                        .setProductKey("81112103")
                        .setUnitPrice(45000)
        ))
        .setTerms("50% de anticipo, entrega en 4 semanas.")
        .setAutoInvoice(true), RequestOptions.idempotencyKey("pedido-A-1042"));
System.out.println(quote.getId());
```

## Respuesta 201

Correcto.

Un objeto `Quote`:

| Campo | Tipo | Siempre viene | Descripción |
| --- | --- | --- | --- |
| `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). |
| `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. |
| `object` | `"quote"` | sí | Qué tipo de objeto es («invoice», «customer»…). |
| `issuer` | string | sí | El emisor (iss_…). |
| `number` | string | sí | Su serie y folio, como lo ve el cliente (COT-12). |
| `version` | integer | sí | Cambiar una ya enviada hace una versión nueva con el mismo número; el id contesta siempre la última. (de -9007199254740991 a 9007199254740991) |
| `status` | `draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired`, `invoiced` | sí | draft, sent, viewed (el cliente abrió la liga), accepted, rejected, expired (pasó valid_until) o invoiced. |
| `url` | string o null | sí | La liga donde el cliente la ve, descarga su PDF, la acepta, pide cambios o la rechaza; null mientras sea borrador. |
| `customer` | objeto | sí | A quién va: tax_id null si aún no tiene sus datos fiscales; catalog_id es su cliente (cus_…) en su catálogo, o null. |
| `customer.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. |
| `customer.name` | string o null | sí | Nombre. |
| `customer.email` | string o null | sí | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. |
| `customer.catalog_id` | string o null | sí | El cliente (cus_…) en su catálogo, o null si no está en él. |
| `currency` | string | sí | La moneda. |
| `items` | arreglo de objeto | sí | Los conceptos, como los de una factura. |
| `items[].description` | string | sí | Descripción del concepto (Descripcion), hasta 1,000 caracteres. |
| `items[].product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… |
| `items[].quantity` | number |  | Cantidad (Cantidad). Por omisión 1. |
| `items[].unit_key` | string |  | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… |
| `items[].unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). |
| `total` | number o null | sí | El total con impuestos. |
| `valid_until` | string | sí | Su último día de validez (AAAA-MM-DD). |
| `terms` | string o null | sí | Las condiciones impresas: entrega, anticipo, garantía… |
| `notes` | string o null | sí | Las notas, como las de una factura. |
| `auto_invoice` | boolean | sí | Si al aceptarla se timbra la factura sola. |
| `customer_comment` | string o null | sí | Lo que escribió el cliente al pedir cambios o al rechazarla, o null. |
| `sent_at` | string (date-time) o null | sí | Cuándo se envió. |
| `viewed_at` | string (date-time) o null | sí | Cuándo el cliente abrió la liga por primera vez. |
| `decided_at` | string (date-time) o null | sí | Cuándo se aceptó o rechazó. |
| `decided_by` | `customer`, `user` o null | sí | customer (en su liga) o user (usted, con POST /v1/quotes/{id}/accept o desde la app). |
| `invoice` | string o null | sí | La factura (inv_…) que se timbró de ella, o null. |
| `created_at` | string (date-time) o null | sí | Cuándo se creó. |

```json
{
  "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx",
  "object": "quote",
  "livemode": false,
  "issuer": "iss_OufpKqnrvx9p",
  "number": "COT-12",
  "version": 1,
  "status": "sent",
  "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq",
  "customer": {
    "tax_id": "ICV060329BY0",
    "name": "INMOBILIARIA CVA",
    "email": "compras@cva.example",
    "catalog_id": "cus_OufpKqnrvx9p42"
  },
  "currency": "MXN",
  "items": [
    {
      "description": "Desarrollo del sitio web",
      "product_key": "81112103",
      "quantity": 1,
      "unit_key": "E48",
      "unit_price": 45000
    }
  ],
  "total": 52200,
  "valid_until": "2026-11-09",
  "terms": "50% de anticipo, entrega en 4 semanas.",
  "notes": null,
  "auto_invoice": true,
  "customer_comment": null,
  "sent_at": "2026-10-25T10:12:00-06:00",
  "viewed_at": null,
  "decided_at": null,
  "decided_by": null,
  "invoice": null,
  "created_at": "2026-10-25T10:05:00-06:00"
}
```

## Errores

Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md.
