# Cotizaciones

> Mande una cotización con liga: su cliente la ve, la acepta, pide cambios o la rechaza, y la factura se timbra con los mismos conceptos.

Una **cotización** lleva los conceptos y las condiciones de pago de una factura. Su cliente la recibe con una liga donde la ve, descarga el PDF, la **acepta**, **pide cambios** o la **rechaza**, y usted se entera por [webhooks](https://fiscalisto.com/docs/api/webhooks). Al aceptarla, la factura sale de los mismos conceptos: sola, con `auto_invoice`, o cuando usted la pida.

## Crear una

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/quotes \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": {
      "tax_id": "ICV060329BY0",
      "legal_name": "INMOBILIARIA CVA",
      "tax_system": "601",
      "zip": "33826",
      "email": "compras@cva.example"
    },
    "payment_form": "03",
    "items": [
      { "description": "Desarrollo del sitio web", "product_key": "81112103", "unit_price": 45000 }
    ],
    "terms": "50% de anticipo, entrega en 4 semanas.",
    "valid_until": "2026-11-30"
  }'
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

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

const quote = await fiscalisto.quotes.create({
  customer: {
    tax_id: "ICV060329BY0",
    legal_name: "INMOBILIARIA CVA",
    tax_system: "601",
    zip: "33826",
    email: "compras@cva.example",
  },
  payment_form: "03",
  items: [
    {
      description: "Desarrollo del sitio web",
      product_key: "81112103",
      unit_price: 45000,
    },
  ],
  terms: "50% de anticipo, entrega en 4 semanas.",
  valid_until: "2026-11-30",
});
```

```python tab="Python"
from fiscalisto import Fiscalisto

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

quote = fiscalisto.quotes.create({
    "customer": {
        "tax_id": "ICV060329BY0",
        "legal_name": "INMOBILIARIA CVA",
        "tax_system": "601",
        "zip": "33826",
        "email": "compras@cva.example",
    },
    "payment_form": "03",
    "items": [
        {
            "description": "Desarrollo del sitio web",
            "product_key": "81112103",
            "unit_price": 45000,
        },
    ],
    "terms": "50% de anticipo, entrega en 4 semanas.",
    "valid_until": "2026-11-30",
})
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

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

$quote = $fiscalisto->quotes->create([
    'customer' => [
        'tax_id' => 'ICV060329BY0',
        'legal_name' => 'INMOBILIARIA CVA',
        'tax_system' => '601',
        'zip' => '33826',
        'email' => 'compras@cva.example',
    ],
    'payment_form' => '03',
    'items' => [
        [
            'description' => 'Desarrollo del sitio web',
            'product_key' => '81112103',
            'unit_price' => 45000,
        ],
    ],
    'terms' => '50% de anticipo, entrega en 4 semanas.',
    'valid_until' => '2026-11-30',
]);
```

```go tab="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(`{"tax_id":"ICV060329BY0","legal_name":"INMOBILIARIA CVA","tax_system":"601","zip":"33826","email":"compras@cva.example"}`),
		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."),
		ValidUntil: fiscalisto.String("2026-11-30"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(quote.ID)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.model.CreateQuoteParams;
import com.fiscalisto.model.CreateQuoteParamsItem;
import java.util.List;
import java.util.Map;

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

var quote = fiscalisto.quotes().create(new CreateQuoteParams()
        .setCustomer(Map.of(
                "tax_id", "ICV060329BY0",
                "legal_name", "INMOBILIARIA CVA",
                "tax_system", "601",
                "zip", "33826",
                "email", "compras@cva.example"
        ))
        .setPaymentForm("03")
        .setItems(List.of(
                new CreateQuoteParamsItem()
                        .setDescription("Desarrollo del sitio web")
                        .setProductKey("81112103")
                        .setUnitPrice(45000)
        ))
        .setTerms("50% de anticipo, entrega en 4 semanas.")
        .setValidUntil("2026-11-30"));
System.out.println(quote.getId());
```

```csharp tab="C#"
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("{\"tax_id\":\"ICV060329BY0\",\"legal_name\":\"INMOBILIARIA CVA\",\"tax_system\":\"601\",\"zip\":\"33826\",\"email\":\"compras@cva.example\"}").RootElement,
    PaymentForm = "03",
    Items = new()
    {
        new()
        {
            Description = "Desarrollo del sitio web",
            ProductKey = "81112103",
            UnitPrice = 45000,
        },
    },
    Terms = "50% de anticipo, entrega en 4 semanas.",
    ValidUntil = "2026-11-30",
});
```

`items`, `payment_form`, `payment_method`, `use`, `currency` y `notes` son los de [POST /v1/invoices](https://fiscalisto.com/docs/api/facturas) y se revisan con las mismas reglas, así que una cotización siempre se puede facturar. `valid_until` es su último día (por omisión, en 15 días; hasta un año) y `terms` sus condiciones, impresas en el PDF.

`customer` puede ser:

- Un cliente de su catálogo: `"customer": "cus_…"`.
- Sus datos fiscales, como en una factura: se agrega a sus clientes.
- Solo `name` y `email`, si aún no le da sus datos fiscales: `{ "name": "Juan Pérez", "email": "juan@correo.example" }`. Puede agregárselos después, o mandarla con `auto_invoice` para que los dé él al aceptarla.

Contesta `201` con la cotización (`quo_…`) en borrador: su número (`COT-12`), el total con impuestos y `url: null` hasta que la mande.

## Mandarla

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/quotes/quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx/send \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Le comparto la cotización del sitio.", "auto_invoice": true }'
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

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

const quote = await fiscalisto.quotes.send("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", {
  message: "Le comparto la cotización del sitio.",
  auto_invoice: true,
});
```

```python tab="Python"
from fiscalisto import Fiscalisto

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

quote = fiscalisto.quotes.send("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", {
    "message": "Le comparto la cotización del sitio.",
    "auto_invoice": True,
})
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

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

$quote = $fiscalisto->quotes->send('quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx', [
    'message' => 'Le comparto la cotización del sitio.',
    'auto_invoice' => true,
]);
```

```go tab="Go"
package main

import (
	"context"
	"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.Send(ctx, "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", &fiscalisto.SendQuoteParams{
		Message:     fiscalisto.String("Le comparto la cotización del sitio."),
		AutoInvoice: fiscalisto.Bool(true),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(quote.ID)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.model.SendQuoteParams;

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

var quote = fiscalisto.quotes().send("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", new SendQuoteParams()
        .setMessage("Le comparto la cotización del sitio.")
        .setAutoInvoice(true));
System.out.println(quote.getId());
```

```csharp tab="C#"
using Fiscalisto;

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

var quote = await fiscalisto.Quotes.SendAsync("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", new SendQuoteParams
{
    Message = "Le comparto la cotización del sitio.",
    AutoInvoice = true,
});
```

Llega al correo del cliente (o a `to`, hasta 5 correos) con el PDF y la liga, que también viene en `url`. Con `"send_email": false` solo se marca enviada y contesta `url`, para que usted la mande por WhatsApp o desde su sistema. En pruebas el correo solo se envía a las personas de su cuenta API, como el de una factura.

Con `auto_invoice: true`, al aceptarla la factura se timbra sola con los timbres de su cuenta: de inmediato si ya tiene los datos fiscales del cliente, o en cuanto los dé en la misma página si no. Una cotización en otra moneda no se timbra sola: el tipo de cambio de ese día puede no ser el de hoy.

## Lo que hace su cliente

| Evento | Cuándo |
| --- | --- |
| `quote.viewed` | Abre la liga por primera vez (`status: viewed`). |
| `quote.changes_requested` | Pide cambios; lo que escribió viene en `customer_comment`. |
| `quote.accepted` | La acepta (`decided_by: customer`). Con `auto_invoice`, después llega `invoice.stamped`. |
| `quote.rejected` | La rechaza, con su motivo en `customer_comment` si lo dio. |

Si le dijo que sí por teléfono, márquela con `POST /v1/quotes/{id}/accept` (`decided_by: user`).

## Cambiarla

`POST /v1/quotes/{id}` cambia solo lo que mande. Un borrador cambia ahí mismo. Una que ya se envió se vuelve una **versión nueva** en borrador, con el mismo número y el mismo id (`version` sube); su cliente sigue viendo la anterior hasta que mande la nueva con `/send`. Una aceptada, rechazada o facturada ya no cambia (`400 quote_invalid`).

Una cotización vencida (`status: expired`) se revive cambiándole `valid_until` y mandándola otra vez.

## Facturarla

```bash tab="cURL"
curl -X POST https://api.fiscalisto.com/v1/quotes/quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx/invoice \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Idempotency-Key: cot-12-factura"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

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

const invoice = await fiscalisto.quotes.invoice("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", { idempotencyKey: "cot-12-factura" });
```

```python tab="Python"
from fiscalisto import Fiscalisto

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

invoice = fiscalisto.quotes.invoice("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", idempotency_key="cot-12-factura")
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

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

$invoice = $fiscalisto->quotes->invoice('quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx', ['idempotency_key' => 'cot-12-factura']);
```

```go tab="Go"
package main

import (
	"context"
	"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()

	invoice, err := client.Quotes.Invoice(ctx, "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", fiscalisto.WithIdempotencyKey("cot-12-factura"))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(invoice.ID)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.RequestOptions;

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

var invoice = fiscalisto.quotes().invoice("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", RequestOptions.idempotencyKey("cot-12-factura"));
System.out.println(invoice.getId());
```

```csharp tab="C#"
using Fiscalisto;

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

var invoice = await fiscalisto.Quotes.InvoiceAsync("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", new RequestOptions { IdempotencyKey = "cot-12-factura" });
```

Timbra la factura con sus conceptos y los datos fiscales del cliente y contesta la factura (`201`). Si no estaba aceptada, la marca aceptada; la cotización queda `invoiced` con la factura en `invoice`. Si ya se facturó, contesta esa misma factura: nunca se timbran dos. Sin los datos fiscales del cliente contesta `400 quote_customer_missing`; rechazada o vencida, `400 quote_not_invoiceable`.

## Lo demás

- `GET /v1/quotes` las lista, las más recientes primero, cada una en su última versión; filtre con `status` (`draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired` o `invoiced`).
- `GET /v1/quotes/{id}/pdf` descarga el PDF, con el diseño de PDF del emisor.
- `DELETE /v1/quotes/{id}` borra una que nunca se envió; si ya se envió, `400 quote_not_draft`.

Una cuenta crea hasta 20,000 cotizaciones al día en producción y 200 en pruebas ([Límites](https://fiscalisto.com/docs/api/limites)).
