> For the complete documentation index, see [llms.txt](https://docs.zipnova.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zipnova.com/carriers/como-se-cotiza.md).

# Cómo se cotiza

Cómo calcula Zipnova el precio de tu Carrier con tarifario fijo, callback a tu API y fallback.

Cuando un seller cotiza un envío, Zipnova evalúa los tarifarios **activos** de los Carriers habilitados para esa cuenta. Cada tarifario cotiza de una de dos formas, según su `rate_source`:

* **`table`**: con las zonas que publicaste. Es la opción más simple.
* **`api`**: llamando al callback de cotización de tu Carrier, para calcular el precio en el momento.

```mermaid
flowchart TD
    A["Un seller cotiza un envío"] --> B["Zipnova evalúa los tarifarios activos<br/>de los Carriers habilitados para la cuenta"]
    B --> C{"rate_source"}
    C -->|"table"| D["Cotiza con las zonas publicadas"]
    C -->|"api"| E["Llama al callback de cotización<br/>timeout de 500 ms"]
    E -->|"200 con price"| F["Usa el precio del Carrier"]
    E -->|"204 o available false"| G["El Carrier no cubre el envío:<br/>no se cotiza"]
    E -->|"Timeout, 4xx, 5xx o respuesta inválida"| H{"¿Tiene zonas publicadas?"}
    H -->|"Sí"| D
    H -->|"No"| I["El Carrier queda fuera de esta cotización"]
```

{% hint style="info" %}
Un Carrier puede tener como máximo un tarifario `api` por tipo de servicio (activo o no). Los tarifarios `table` no tienen límite.
{% endhint %}

## Tarifario fijo (`table`)

Un tarifario se crea **inactivo**. El orden es: crearlo con `POST /tariffs`, publicar sus zonas con `PUT /tariffs/{tariff_id}/rates` y recién entonces activarlo con `PUT /tariffs/{tariff_id}`.

### Zonas

Cada zona define:

* **Rangos de costo** (`costs`): escalones por peso en gramos (`tariff_type: weight`) o por volumen en cm³ (`tariff_type: volume`). `max_cargo` es el tope de cada rango y `value`, su importe en la moneda del país.
* **Destinos** (`destinations`): lo que cubre la zona, con su plazo de entrega mínimo y máximo en días.
* **Orígenes** (`origins`): desde dónde aplica la zona.

Las ubicaciones pueden ser de tipo `zipcode`, `city_id`, `state` o `country`.

```json
{
  "mode": "upsert",
  "zones": [
    {
      "name": "Zona 1",
      "costs": [
        { "max_cargo": 5000, "value": 4500 },
        { "max_cargo": 10000, "value": 6200 }
      ],
      "destinations": [
        { "type": "zipcode", "value": "1414", "delivery_time_min": 1, "delivery_time_max": 3 }
      ],
      "origins": [
        { "type": "zipcode", "value": "1736" }
      ]
    }
  ]
}
```

### Publicación de zonas

* Con `mode: replace` (el valor por defecto) se reemplazan todas las zonas del tarifario. Con `mode: upsert` se reemplazan solo las zonas enviadas, identificadas por nombre, y el resto queda intacto.
* La publicación se procesa en segundo plano: `PUT .../rates` responde `202`. Consulta el resultado con `GET /tariffs/{tariff_id}/imports/{import_id}` hasta que el estado sea `finished` o `failed`.
* Si falla, no se aplica ningún cambio y `errors` detalla los problemas encontrados (hasta 100).
* En un Carrier `open` ya aprobado por Zipnova, el cambio requiere [aprobación de Operaciones](/carriers/visibilidad-y-modalidades-de-acceso.md).

### Parámetros del tarifario

| Parámetro                                            | Para qué sirve                                                                                                                                                                                      |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `use_volumetric_weight` y `volumetric_weight_factor` | Cotizar por el mayor entre el peso real y el peso volumétrico. El peso volumétrico es el volumen en m³ por el factor en kg/m³. Por ejemplo, 48.000 cm³ son 0,048 m³: con factor 250 resultan 12 kg. |
| `insurance_rate` e `insurance_min_amount`            | Seguro como porcentaje del valor declarado, con un importe mínimo por envío.                                                                                                                        |
| `max_package_*`, `min_shipment_*` y `max_shipment_*` | Límites por bulto y por envío en peso, volumen y dimensiones. Un límite en `null` no restringe.                                                                                                     |
| `min_declared_value` y `max_declared_value`          | Rango de valor declarado que acepta el tarifario.                                                                                                                                                   |

La lista completa está en [Tarifarios](/carriers/referencia-api/tarifarios.md).

## Tarifario por API (`api`)

Para cotizar por API, configura la URL de cotización de tu Carrier en el Carrier Hub. Zipnova la llama una vez por cada tarifario `api` que aplica a la cotización, con un pedido [firmado](/carriers/endpoints/validar-la-firma-de-zipnova.md). El detalle del pedido está en [Cotización](/carriers/endpoints/cotizacion.md).

Tu respuesta es el precio del envío:

```json
{
  "price": 4350.5,
  "delivery_time_min": 2,
  "delivery_time_max": 4,
  "custom_data": { "service_code": "EXP", "quote_ref": "Q-8841" }
}
```

| Regla                  | Detalle                                                                                                                           |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Tiempo máximo          | 500 ms en total.                                                                                                                  |
| Precio                 | Mayor a 0 y hasta 100.000.000, en la moneda del país.                                                                             |
| Plazos                 | `delivery_time_min` y `delivery_time_max` entre 0 y 365 días. Por defecto, el mínimo es 1 y el máximo es el mínimo informado o 3. |
| `custom_data`          | Hasta 2 KB en JSON. Se guarda con el envío si se crea con esa cotización y se devuelve en `GET /shipments/{shipment_id}`.         |
| Tamaño de la respuesta | Hasta 16 KB.                                                                                                                      |
| Envío sin cobertura    | Responde `204` o `{"available": false}`: no se cotiza y no se usa el tarifario fijo.                                              |
| Redirecciones          | No se siguen.                                                                                                                     |

Zipnova no guarda el contenido de las cotizaciones, solo el resultado HTTP.

### Fallback y circuit breaker

Si no respondes a tiempo, respondes `4xx` o `5xx`, o tu respuesta es inválida o está fuera de estas reglas, Zipnova cotiza con tu tarifario fijo, si tiene zonas publicadas. Si no las tiene, tu Carrier queda fuera de esa cotización. Por eso conviene mantener zonas publicadas como respaldo.

Cada uno de esos casos cuenta como error. Si la tasa de errores es alta, Zipnova suspende el callback y avisa por mail. Puedes verlo en `quote_callback_suspended` de `GET /me`, y se reactiva desde el Carrier Hub.
