# Clasificar API V2

API V2 es la versión actual de Clasificar. Autenticá tus consultas con una API key y consultá los permisos y cupos de tu cuenta en el endpoint de usage.

## API v2

Documentación: https://clasific.ar/docs

Consultá información básica de un vehículo por patente y sumá sólo los módulos que necesitás: multas, VTV/RTO, deuda de patente y GNC.

También podés consultar el catálogo de marcas, modelos, versiones y años sin una patente. Está incluido en los planes pagos y no consume cupos de consultas.

- [Consultar el catálogo de vehículos](https://clasific.ar/docs/v2/catalog)

### Una API, dos tipos de consulta

Con V2 podés consultar los datos básicos de una patente o pedir información adicional mediante módulos. Ambos consumos son independientes.

#### Datos del vehículo

```text
GET /v2/vehicles/:plate
```

Devuelve la información disponible para la patente consultada. Podés agregar opciones como codia=true o sheet=true si tu plan las incluye.

#### Módulos

```text
POST /v2/vehicles/:plate/modules
```

Consultá una o varias fuentes adicionales sin necesidad de hacer antes una consulta básica del vehículo.

- Multas — fines
- VTV / RTO — vtv
- Deuda de patente — tax_debt
- GNC — gnc

### Consultas asincrónicas

Los módulos pueden tardar algunos segundos mientras consultamos las distintas fuentes.

Al iniciar una consulta recibís un ID de operación. Usalo para consultar el estado y obtener el resultado:

```text
GET /v2/operations/:id
```

También podés recibir el resultado mediante webhook. Consultar una operación no consume nuevamente tu cupo de módulos.

- [Configurar un webhook](https://clasific.ar/docs/v2/webhooks)

### Cómo se cuenta el uso

El consumo es simple.

```text
GET /v2/vehicles/ZZ000ZZ
```

→ 1 consulta de patente

```text
POST /v2/vehicles/ZZ000ZZ/modules
{"modules":["fines","vtv","tax_debt"]}
```

→ 3 consultas de módulos

Consultar módulos no consume una consulta de patente.

Los límites dependen de tu plan y se comparten entre todas las API keys de tu cuenta.

- [Hacer mi primera consulta →](https://clasific.ar/docs/v2/quickstart)

## Quickstart

Documentación: https://clasific.ar/docs/v2/quickstart

Necesitás una API key activa. Si todavía no tenés una, podés crearla desde Cuenta → API Keys.

- [Cuenta → API Keys](https://clasific.ar/account/keys)

### 1. Consultá un vehículo

Usá la patente directamente en la URL:

```bash
curl 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ' \
  -H 'x-api-key: <TU_API_KEY>'
```

La respuesta contiene la información básica disponible para el vehículo.

Cada request aceptado a este endpoint consume 1 consulta de patente.

### 2. Consultá módulos adicionales

Podés solicitar uno o varios módulos para la misma patente:

```bash
curl -X POST 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ/modules' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"modules":["fines","vtv","tax_debt"]}'
```

Cada módulo solicitado consume 1 consulta de módulo. En este ejemplo se consumen 3.

No necesitás consultar primero los datos básicos del vehículo.

La API devuelve una operación. Copiá el valor de id de la respuesta para consultar su estado en el siguiente paso.

### 3. Obtené el resultado

Reemplazá <OPERATION_ID> por el id recibido y consultá la operación hasta que termine:

```bash
curl 'https://api.clasific.ar/v2/operations/<OPERATION_ID>' \
  -H 'x-api-key: <TU_API_KEY>'
```

Mientras la operación está en curso, su estado es queued o processing. Dejá de hacer polling cuando recibas completed, partial o failed.

Un módulo también puede indicar partial cuando obtuvo resultados pero alguna de sus fuentes no estuvo disponible.

El polling no consume consultas de patente ni de módulos. Cada request cuenta para el límite de requests por minuto de tu plan.

### Repetir una consulta mientras está en curso

Si volvés a solicitar para la misma patente exactamente el mismo conjunto de módulos mientras la operación sigue activa, Clasificar devuelve la operación existente en lugar de iniciar otra.

```text
ZZ000ZZ + fines + vtv
ZZ000ZZ + vtv + fines
```

Estos dos pedidos se consideran la misma consulta mientras esté en curso. No se inicia nuevamente el trabajo y no se consume cupo adicional de módulos.

Cuando una operación ya terminó, una nueva solicitud vuelve a ejecutar los módulos y consume el cupo correspondiente.

## Autenticación

Documentación: https://clasific.ar/docs/v2/authentication

Podés crear y administrar tus credenciales desde Cuenta → API Keys.

- [Cuenta → API Keys](https://clasific.ar/account/keys)

### Usar una API key

En cada request autenticado enviá tu API key mediante el header:

```text
x-api-key: <TU_API_KEY>
```

```bash
curl 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ' \
  -H 'x-api-key: <TU_API_KEY>'
```

Las API keys deben utilizarse desde tu servidor. No las expongas en aplicaciones frontend, repositorios públicos ni código distribuido al cliente.

### Ambientes

| Ambiente | URL base |
| --- | --- |
| Producción | https://api.clasific.ar |
| Sandbox | https://sandbox.clasific.ar |

El entorno Sandbox utiliza datos de prueba y tiene consumo separado de Producción. Usá la misma estructura de endpoints en ambos ambientes:

```text
https://api.clasific.ar/v2/...
https://sandbox.clasific.ar/v2/...
```

### Cuenta y límites

Todas las API keys de una misma cuenta comparten, dentro de cada ambiente:

- El plan contratado.
- Los cupos de consultas.
- Los límites de requests por minuto.
- Las operaciones creadas.

Podés rotar o crear nuevas API keys sin reiniciar el consumo de tu cuenta.

### Errores de autenticación

Una API key ausente o inválida devuelve HTTP 401 Unauthorized. Una key revocada deja de poder utilizarse inmediatamente.

```json
{
  "error": {
    "code": "invalid_api_key",
    "message": "An active x-api-key is required.",
    "requestId": "req_example",
    "retryable": false
  }
}
```

Revisá que estés enviando una API key activa y que el header esté escrito como x-api-key.

## Consultas de patente

Documentación: https://clasific.ar/docs/v2/vehicles

### Consultar un vehículo

Usá la patente directamente en la URL: `GET /v2/vehicles/:plate`.

```bash
curl 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ' \
  -H 'x-api-key: <TU_API_KEY>'
```

La API normaliza la patente y valida los formatos argentinos soportados antes de realizar la consulta. Se admiten patentes de autos en formato antiguo y Mercosur.

Cada consulta aceptada consume 1 consulta de patente. Los requests rechazados antes de ser aceptados no consumen cupo de consultas.

### Respuesta

La respuesta indica si hay información disponible para la patente:

- success indica que el request se procesó correctamente.
- found indica si encontramos información.
- data contiene los datos del vehículo.

Ejemplo abreviado con datos de prueba; la respuesta completa puede incluir más campos:

```json
{
  "success": true,
  "found": true,
  "data": {
    "plate": "ZZ000ZZ",
    "make": "TOYOTA",
    "model": "COROLLA 1.8",
    "year": 2020
  }
}
```

Si no encontramos información disponible:

```json
{
  "success": true,
  "found": false,
  "data": null
}
```

found: false significa que la consulta fue válida pero no encontramos información disponible para esa patente. Es una consulta procesada normalmente y consume 1 consulta de patente.

### Opciones

Podés agregar opciones mediante query parameters para enriquecer la respuesta. Por ejemplo: ?codia=true o ?sheet=true.

| Parámetro | Descripción |
| --- | --- |
| codia | Agrega información de identificación CODIA cuando está disponible. |
| sheet | Agrega información de ficha técnica. |
| tires | Agrega medidas de neumáticos disponibles. |
| classification | Agrega la clasificación pública del vehículo. |
| debug | Agrega información adicional de diagnóstico cuando está disponible. |
| onMiss=search | Si no encontramos datos, inicia una búsqueda adicional y puede devolver una operación asincrónica. |
| sandbox_scenario | Sólo Sandbox. Elegí un escenario: default, clean, debt, partial, unavailable o failed. |
| sandbox_seed | Sólo Sandbox. Fija datos reproducibles para pruebas; hasta 128 caracteres. |

codia, sheet, tires y onMiss=search están disponibles en planes pagos y Custom.

debug=true puede agregar información de diagnóstico en data.debug. No cambia la consulta ni su consumo.

### Buscar cuando no hay datos

Si no encontramos información disponible, podés solicitar una búsqueda adicional con onMiss=search:

```bash
curl 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ?onMiss=search' \
  -H 'x-api-key: <TU_API_KEY>'
```

Si ya hay información suficiente, recibís la respuesta normal. Si hace falta buscar más, la API responde HTTP 202 con una operación asincrónica:

```json
{
  "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa",
  "kind": "vehicle",
  "status": "queued",
  "plate": "ZZ000ZZ",
  "result": null,
  "createdAt": "2026-09-06T12:00:00.000Z",
  "updatedAt": "2026-09-06T12:00:00.000Z"
}
```

Usá el ID para consultar el resultado:

```text
GET /v2/operations/:id
```

El polling no vuelve a consumir una consulta de patente. Cuando la operación termina, result contiene el resultado de la búsqueda.

- [Consultar una operación →](https://clasific.ar/docs/v2/operations)

Para ver todos los campos, parámetros y códigos de respuesta, consultá la referencia OpenAPI.

- [Referencia OpenAPI →](https://api.clasific.ar/v2/openapi.json)

## Catálogo de vehículos

Documentación: https://clasific.ar/docs/v2/catalog

### Acceso y consumo

Consultá marcas, modelos, versiones con su código CODIA y años disponibles, sin necesidad de una patente.

Disponible para cuentas activas con plan Basic, Growth, Scale o Custom. Las consultas no consumen cupos ni generan cargos adicionales; sólo cuentan para el límite de requests por minuto de la cuenta. Podés usarlas aunque hayas agotado tus cupos de consultas.

Autenticá cada request con el header x-api-key. El plan Free no tiene acceso al catálogo, tampoco en Sandbox.

- [Autenticación](https://clasific.ar/docs/v2/authentication)
- [Rate limits y reintentos](https://clasific.ar/docs/v2/rate-limits)

### Consultar marcas

Devuelve las marcas disponibles, ordenadas por nombre. No requiere parámetros.

```bash
curl 'https://api.clasific.ar/v2/catalog/brands' \
  -H 'x-api-key: <TU_API_KEY>'
```

Respuesta HTTP 200 abreviada; se muestran tres marcas de la lista:

```json
{
  "data": [
    {
      "id": "51",
      "name": "FERRARI"
    },
    {
      "id": "17",
      "name": "FIAT"
    },
    {
      "id": "18",
      "name": "FORD"
    }
  ],
  "meta": {
    "catalogVersion": "cat_9fbed2e872381f2fd54e92f9"
  }
}
```

Usá el id de la marca como brandId para consultar sus modelos.

### Consultar modelos

Devuelve los modelos de una marca, ordenados por nombre. El parámetro brandId es obligatorio. Por ejemplo, para Fiat:

```bash
curl 'https://api.clasific.ar/v2/catalog/models?brandId=17' \
  -H 'x-api-key: <TU_API_KEY>'
```

Respuesta HTTP 200 abreviada; se muestran tres modelos de Fiat:

```json
{
  "data": [
    {
      "id": "mdl_17_30",
      "name": "BRAVO"
    },
    {
      "id": "mdl_17_52",
      "name": "CRONOS"
    },
    {
      "id": "mdl_17_42",
      "name": "DOBLO"
    }
  ],
  "meta": {
    "catalogVersion": "cat_9fbed2e872381f2fd54e92f9"
  }
}
```

Usá el id del modelo como único parámetro modelId para consultar las versiones.

### Consultar versiones

Devuelve las versiones de un modelo, ordenadas por nombre. Cada versión incluye los campos id, name y codia (su código CODIA). El único parámetro obligatorio es modelId. Por ejemplo, para Fiat Cronos:

```bash
curl 'https://api.clasific.ar/v2/catalog/versions?modelId=mdl_17_52' \
  -H 'x-api-key: <TU_API_KEY>'
```

Respuesta HTTP 200:

```json
{
  "data": [
    {
      "id": "ia_170846",
      "name": "1.3 ATTRACTIVE",
      "codia": "170846"
    },
    {
      "id": "ia_170858",
      "name": "1.3 ATTRACTIVE L/22",
      "codia": "170858"
    },
    {
      "id": "ia_170801",
      "name": "1.3 DRIVE FIREFLY",
      "codia": "170801"
    },
    {
      "id": "ia_170810",
      "name": "1.3 DRIVE GSE",
      "codia": "170810"
    },
    {
      "id": "ia_170823",
      "name": "1.3 DRIVE GSE PACK CONECTI.",
      "codia": "170823"
    },
    {
      "id": "ia_170835",
      "name": "1.3 DRIVE GSE PACK CONECTIVIDAD L/21",
      "codia": "170835"
    },
    {
      "id": "ia_170843",
      "name": "1.3 DRIVE GSE S-DESIGN",
      "codia": "170843"
    },
    {
      "id": "ia_170859",
      "name": "1.3 DRIVE L/22",
      "codia": "170859"
    },
    {
      "id": "ia_170860",
      "name": "1.3 DRIVE L/22 CVT",
      "codia": "170860"
    },
    {
      "id": "ia_170861",
      "name": "1.3 DRIVE L/22 S-DESIGN",
      "codia": "170861"
    },
    {
      "id": "ia_170890",
      "name": "1.3 DRIVE L/25 PACK PLUS",
      "codia": "170890"
    },
    {
      "id": "ia_170891",
      "name": "1.3 DRIVE L/25 PACK PLUS CVT",
      "codia": "170891"
    },
    {
      "id": "ia_170866",
      "name": "1.3 GSE DRIVE PACK PLUS L/23",
      "codia": "170866"
    },
    {
      "id": "ia_170879",
      "name": "1.3 GSE DRIVE PACK PLUS L/23 CVT",
      "codia": "170879"
    },
    {
      "id": "ia_170865",
      "name": "1.3 GSE LIKE L/23",
      "codia": "170865"
    },
    {
      "id": "ia_170867",
      "name": "1.3 GSE STILE L/23",
      "codia": "170867"
    },
    {
      "id": "ia_170889",
      "name": "1.3 LIKE L/25",
      "codia": "170889"
    },
    {
      "id": "ia_170892",
      "name": "1.3 PRECISION CVT L/25",
      "codia": "170892"
    },
    {
      "id": "ia_170862",
      "name": "1.3 PRECISION L/22",
      "codia": "170862"
    },
    {
      "id": "ia_170826",
      "name": "1.8 CENTENARIO AUT E/LIM",
      "codia": "170826"
    },
    {
      "id": "ia_170855",
      "name": "1.8 HGT",
      "codia": "170855"
    },
    {
      "id": "ia_170803",
      "name": "1.8 PRECISION",
      "codia": "170803"
    },
    {
      "id": "ia_170802",
      "name": "1.8 PRECISION AUT",
      "codia": "170802"
    },
    {
      "id": "ia_170837",
      "name": "1.8 PRECISION AUT L/21",
      "codia": "170837"
    },
    {
      "id": "ia_170836",
      "name": "1.8 PRECISION L/21",
      "codia": "170836"
    },
    {
      "id": "ia_170870",
      "name": "1.8 PRECISION L/21 PACK PREMIUM A",
      "codia": "170870"
    },
    {
      "id": "ia_170825",
      "name": "1.8 PRECISION PACK PREMIUM",
      "codia": "170825"
    },
    {
      "id": "ia_170844",
      "name": "1.8 PRECISION PACK PREMIUM L/21",
      "codia": "170844"
    },
    {
      "id": "ia_170845",
      "name": "1.8 PRECISION PACK STYLE L/21",
      "codia": "170845"
    },
    {
      "id": "ia_170824",
      "name": "1.8 PRECISION TECHNOLOGY",
      "codia": "170824"
    },
    {
      "id": "ia_170914",
      "name": "L/26 1.3 CVT",
      "codia": "170914"
    },
    {
      "id": "ia_170913",
      "name": "L/26 1.3 DRIVE PLUS",
      "codia": "170913"
    },
    {
      "id": "ia_170912",
      "name": "L/26 1.3 LIKE",
      "codia": "170912"
    },
    {
      "id": "ia_170915",
      "name": "L/26 1.3 PRECISION CVT",
      "codia": "170915"
    }
  ],
  "meta": {
    "catalogVersion": "cat_9fbed2e872381f2fd54e92f9"
  }
}
```

Cada CODIA identifica una variante y aparece una sola vez, aunque esté disponible en varios años. Usá el id como versionId para consultar esos años.

Las líneas comerciales se mantienen separadas: 1.3 PRECISION CVT L/25 tiene CODIA 170892 y L/26 1.3 PRECISION CVT tiene CODIA 170915. Ambas están disponibles en 2026.

### Consultar años

Devuelve los años disponibles para una versión, de mayor a menor. El único parámetro obligatorio es versionId. Por ejemplo, para Fiat Cronos 1.3 PRECISION CVT L/25 (CODIA 170892):

```bash
curl 'https://api.clasific.ar/v2/catalog/years?versionId=ia_170892' \
  -H 'x-api-key: <TU_API_KEY>'
```

Respuesta HTTP 200:

```json
{
  "data": [
    2026,
    2025
  ],
  "meta": {
    "catalogVersion": "cat_9fbed2e872381f2fd54e92f9"
  }
}
```

Los años son números y corresponden a la versión identificada por ese CODIA. La cobertura depende de cada versión y puede tener años intermedios sin datos.

### Formato de las respuestas

Cada endpoint devuelve la lista completa que corresponde a los parámetros enviados, sin paginación. Los ejemplos anteriores usan datos reales del catálogo; las listas de marcas y modelos están abreviadas.

Marcas y modelos incluyen id y name; las versiones agregan codia. Conservá los IDs y el CODIA como strings. Usá los IDs devueltos por la API, sin construirlos a partir de los nombres. Cada ID de modelo identifica un modelo entre todas las marcas y se mantiene entre actualizaciones del catálogo. Cada ID de versión identifica una variante CODIA; códigos distintos pueden compartir el mismo nombre. Tratá los IDs como valores opacos y enviá los parámetros con la codificación URL habitual.

meta.catalogVersion identifica la revisión del catálogo. Si cambia entre consultas relacionadas, volvé a consultar los IDs utilizados para trabajar con la misma revisión. Las respuestas usan Cache-Control: no-store.

Una marca o un modelo válido sin resultados devuelve data: []. Un ID inexistente devuelve 404 catalog_not_found.

Si integraste el contrato anterior, volvé a obtener los IDs desde /models: los códigos locales como 52 fueron reemplazados por IDs únicos como mdl_17_52. En /versions enviá sólo modelId y en /years sólo versionId; los parámetros anteriores adicionales devuelven 400.

El catálogo describe vehículos y sus años disponibles. No incluye precios ni cotizaciones.

### Sandbox y errores

Para probar en Sandbox, usá https://sandbox.clasific.ar con la misma API key de tu cuenta paga. Los IDs de prueba son demo-brand, mdl_demo-brand_demo-model e ia_000001. La versión incluye codia: "000001" y el endpoint de años devuelve [2026, 2024, 2023]. Son datos sintéticos, con catalogVersion: cat_demo_v3.

Los parámetros obligatorios son los mismos en ambos ambientes. No se aceptan parámetros adicionales ni repetidos.

| HTTP | Código | Descripción |
| --- | --- | --- |
| 400 | invalid_request | Falta un parámetro obligatorio, su formato es inválido o hay parámetros adicionales o repetidos. |
| 401 | invalid_api_key | La API key está ausente, es inválida o fue revocada. |
| 403 | access_denied | La cuenta no está habilitada o no tiene un plan con acceso al catálogo. |
| 404 | catalog_not_found | El ID consultado no existe en el catálogo de ese ambiente. |
| 429 | rate_limit_exceeded | Se alcanzó el límite de requests por minuto. Respetá Retry-After antes de reintentar. |
| 503 | temporarily_unavailable | El catálogo no está disponible temporalmente. Reintentá más tarde. |

## Módulos

Documentación: https://clasific.ar/docs/v2/modules

### Consultar módulos

Usá `POST /v2/vehicles/:plate/modules` para consultar uno o varios módulos para una patente.

No necesitás hacer antes una consulta de patente. Cada módulo solicitado consume 1 consulta de módulo.

```bash
curl -X POST 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ/modules' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"modules":["fines","vtv","tax_debt"]}'
```

### Módulos disponibles

| Módulo | ID |
| --- | --- |
| [Multas](https://clasific.ar/docs/v2/modules/tickets) | fines |
| [VTV / RTO](https://clasific.ar/docs/v2/modules/vtv) | vtv |
| [Deuda de patente](https://clasific.ar/docs/v2/modules/tax-debt) | tax_debt |
| [GNC](https://clasific.ar/docs/v2/modules/cng) | gnc |

### Request

El body recibe una lista de módulos:

```json
{
  "modules": [
    "fines",
    "vtv"
  ]
}
```

Podés solicitar entre 1 y 4 módulos distintos en una misma operación.

### Cómo se cuenta el uso

Cada módulo solicitado consume 1 consulta de módulo.

```json
[
  "fines"
]
```

→ 1 consulta de módulo

```json
[
  "fines",
  "vtv",
  "tax_debt"
]
```

→ 3 consultas de módulos

Consultar módulos no consume consultas de patente.

Si no tenés suficiente cupo para todos los módulos solicitados, la operación no se inicia.

### Resultado de la operación

Las consultas de módulos son asincrónicas. Al iniciar una operación nueva, recibís su ID, estado, patente y módulos solicitados. Por ejemplo, para fines y vtv:

```json
{
  "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa",
  "kind": "modules",
  "status": "queued",
  "plate": "ZZ000ZZ",
  "modules": {
    "fines": {
      "status": "queued",
      "data": null,
      "error": null
    },
    "vtv": {
      "status": "queued",
      "data": null,
      "error": null
    }
  },
  "createdAt": "2026-09-06T12:00:00.000Z",
  "updatedAt": "2026-09-06T12:00:00.000Z"
}
```

Usá el ID para consultar el resultado:

```text
GET /v2/operations/:id
```

También podés recibir el resultado mediante webhook.

- [Operaciones →](https://clasific.ar/docs/v2/operations)
- [Webhooks →](https://clasific.ar/docs/v2/webhooks)

### Repetir una consulta en curso

Si volvés a pedir exactamente los mismos módulos para la misma patente mientras la operación sigue en curso, Clasificar devuelve la operación existente en lugar de iniciar otra.

```text
ZZ000ZZ + fines + vtv
ZZ000ZZ + vtv + fines
```

Estos pedidos se consideran la misma consulta mientras esté activa. No se inicia nuevamente el trabajo y no se consume cupo adicional de módulos.

Cuando la operación termina, una nueva solicitud vuelve a ejecutar los módulos y consume el cupo correspondiente.

Si cambiás la combinación de módulos, se crea una nueva operación.

```text
Primera operación: ["fines"]
Nueva solicitud: ["fines", "vtv"]
→ nueva operación
```

### Resultados parciales

Cada módulo informa su propio estado. Si una fuente no está disponible, otros módulos de la misma operación pueden completarse normalmente.

En algunos casos un módulo puede devolver partial cuando obtuvo información con cobertura reducida.

Para consultar schemas, códigos de respuesta y todos los campos disponibles, revisá la referencia de API.

- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Multas

Documentación: https://clasific.ar/docs/v2/modules/tickets

### Qué devuelve

El módulo de Multas consulta las infracciones disponibles para la patente en las jurisdicciones cubiertas por Clasificar.

### Consultar multas

Usá fines en `POST /v2/vehicles/:plate/modules` para consultar Multas.

```bash
curl -X POST 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ/modules' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"modules":["fines"]}'
```

Cada solicitud nueva consume 1 consulta de módulo.

- [Cómo consultar y combinar módulos →](https://clasific.ar/docs/v2/modules)

### Campos del resultado

Estos campos están dentro de data. Los campos marcados con null pueden no tener información disponible. Las fechas se devuelven como fecha o fecha y hora ISO; si no se pueden interpretar, su valor es null.

| Campo | Tipo | Descripción |
| --- | --- | --- |
| total | number | Cantidad de infracciones en items. |
| amountTotal | number | Suma de amount disponibles, redondeada a dos decimales. No suma importes con descuento. |
| currency | "ARS" | Moneda de los importes: pesos argentinos. |
| items | array | Listado de infracciones. |
| items[].date | string \| null | Fecha de la infracción, cuando está disponible. |
| items[].amount | number \| null | Importe informado en ARS; null si no está disponible. |
| items[].status | string | Estado informado, normalizado; unknown si no se puede identificar. |
| items[].authority | string \| null | Autoridad u organismo informado. |
| items[].reference | string \| null | Referencia de la infracción. |
| items[].description | string \| null | Descripción disponible de la infracción. |
| items[].jurisdiction | string | Jurisdicción asociada al registro. |
| items[].dueDate | string \| null | Fecha de vencimiento disponible. |
| items[].discountedAmount | number \| null | Importe con descuento informado, en ARS. |

### Cobertura y estados

La información disponible depende de la jurisdicción y de la disponibilidad de sus sistemas al momento de la consulta.

completed con total: 0 significa que la consulta se completó correctamente sin encontrar infracciones en las fuentes consultadas. No es un error.

Un resultado sin infracciones no reemplaza un certificado oficial de libre deuda.

Multas puede indicar partial con un warning reduced_coverage cuando se obtuvieron respuestas, pero no se pudo verificar parte relevante de la cobertura. Puede ocurrir incluso si items está vacío.

Si no fue posible completar el módulo, el resultado indica failed e incluye un error.

- [Errores →](https://clasific.ar/docs/v2/errors)

### Ejemplo de respuesta

Este es el resultado de modules.fines dentro de la operación.

Ejemplo con datos de prueba:

```json
{
  "status": "completed",
  "data": {
    "total": 1,
    "amountTotal": 47499.5,
    "currency": "ARS",
    "items": [
      {
        "date": "2024-05-03T15:53:00-03:00",
        "amount": 47499.5,
        "status": "voluntary_payment",
        "authority": null,
        "reference": "I06223680",
        "description": "Conducir sin portar la licencia",
        "jurisdiction": "CABA",
        "dueDate": null,
        "discountedAmount": null
      }
    ]
  },
  "error": null
}
```

Ejemplo de consulta completada sin registros:

```json
{
  "status": "completed",
  "data": {
    "total": 0,
    "amountTotal": 0,
    "currency": "ARS",
    "items": []
  },
  "error": null
}
```

Para ver el schema completo y todos los códigos de respuesta, consultá la referencia de API.

- [Operaciones →](https://clasific.ar/docs/v2/operations)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## VTV / RTO

Documentación: https://clasific.ar/docs/v2/modules/vtv

### Qué devuelve

El módulo VTV / RTO reúne información disponible sobre verificaciones técnicas e inspecciones asociadas a la patente.

### Consultar VTV / RTO

Usá vtv en `POST /v2/vehicles/:plate/modules` para consultar VTV / RTO.

```bash
curl -X POST 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ/modules' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"modules":["vtv"]}'
```

Cada solicitud nueva consume 1 consulta de módulo.

- [Cómo consultar y combinar módulos →](https://clasific.ar/docs/v2/modules)

### Campos del resultado

Estos campos están dentro de data. Los campos marcados con null pueden no tener información disponible. Las fechas se devuelven como fecha o fecha y hora ISO; si no se pueden interpretar, su valor es null.

| Campo | Tipo | Descripción |
| --- | --- | --- |
| total | number | Cantidad de inspecciones en items. |
| items | array | Listado de inspecciones. |
| items[].type | string | Tipo de registro normalizado, por ejemplo vtv o rto; unknown si no se identifica. |
| items[].date | string \| null | Fecha de la inspección disponible. |
| items[].sticker | string \| null | Identificador de oblea informado. |
| items[].inspectionType | string | inspection, reinspection o unknown: inspección, reinspección o tipo no identificado. |
| items[].detail | string \| null | Detalle disponible de la inspección. |
| items[].result | string | Resultado normalizado. Consultá los valores en Resultado de inspección. |
| items[].certificate | string \| null | Identificador de certificado informado. |
| items[].jurisdiction | string | Jurisdicción asociada al registro. |
| items[].facility | string \| null | Planta o taller informado. |
| items[].dueDate | string \| null | Fecha de vencimiento disponible. |

### Resultado de inspección

result describe el resultado informado de cada inspección; no es el status de la consulta.

| Valor | Significado |
| --- | --- |
| approved | Resultado informado como aprobado, apto o vigente. |
| conditional | Resultado informado como condicional. |
| rejected | Resultado informado como rechazado o no apto. |
| expired | Resultado informado como vencido. |
| unknown | No se pudo identificar el resultado informado. |

Estos valores representan la información recibida. No se recalculan como una certificación de vigencia a la fecha de tu consulta.

### Cobertura y estados

La cobertura depende de la jurisdicción y de la disponibilidad de información sobre inspecciones al momento de consultar.

completed con total: 0 significa que la consulta se completó correctamente sin encontrar inspecciones en las fuentes consultadas. No es un error.

Un resultado sin registros no certifica por sí solo la situación técnica actual del vehículo.

Si no fue posible completar el módulo, el resultado indica failed e incluye un error.

- [Errores →](https://clasific.ar/docs/v2/errors)

### Ejemplo de respuesta

Este es el resultado de modules.vtv dentro de la operación.

Ejemplo de consulta completada sin registros:

```json
{
  "status": "completed",
  "data": {
    "total": 0,
    "items": []
  },
  "error": null
}
```

Para ver el schema completo y todos los códigos de respuesta, consultá la referencia de API.

- [Operaciones →](https://clasific.ar/docs/v2/operations)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Deuda de patente

Documentación: https://clasific.ar/docs/v2/modules/tax-debt

### Qué devuelve

El módulo de Deuda de patente consulta obligaciones e impuestos vehiculares disponibles para la patente en las jurisdicciones cubiertas.

### Consultar deuda de patente

Usá tax_debt en `POST /v2/vehicles/:plate/modules` para consultar Deuda de patente.

```bash
curl -X POST 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ/modules' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"modules":["tax_debt"]}'
```

Cada solicitud nueva consume 1 consulta de módulo.

- [Cómo consultar y combinar módulos →](https://clasific.ar/docs/v2/modules)

### Campos del resultado

Estos campos están dentro de data. Los campos marcados con null pueden no tener información disponible. Las fechas se devuelven como fecha o fecha y hora ISO; si no se pueden interpretar, su valor es null.

| Campo | Tipo | Descripción |
| --- | --- | --- |
| total | number | Cantidad de obligaciones en items. |
| amountTotal | number | Suma updatedAmount cuando está disponible; en cada otro registro usa amount. Los importes ausentes no se suman. Total redondeado a dos decimales. |
| currency | "ARS" | Moneda de los importes: pesos argentinos. |
| items | array | Listado de obligaciones. |
| items[].period | string \| null | Período de la obligación tal como fue informado. |
| items[].description | string \| null | Descripción disponible del impuesto u obligación. |
| items[].dueDate | string \| null | Fecha de vencimiento disponible. |
| items[].amount | number \| null | Importe informado en ARS. |
| items[].updatedAmount | number \| null | Importe actualizado informado, en ARS, cuando está disponible. |
| items[].status | string | Estado informado, normalizado; unknown si no se puede identificar. |
| items[].jurisdiction | string | Jurisdicción asociada a la obligación. |

### Cobertura y estados

La información disponible depende de la jurisdicción y de la disponibilidad de los organismos consultados.

completed con total: 0 significa que la consulta se completó correctamente sin encontrar obligaciones en las fuentes consultadas. No es un error.

Un resultado sin obligaciones no reemplaza un certificado oficial de libre deuda.

Si no fue posible completar el módulo, el resultado indica failed e incluye un error.

- [Errores →](https://clasific.ar/docs/v2/errors)

### Ejemplo de respuesta

Este es el resultado de modules.tax_debt dentro de la operación.

Ejemplo con datos de prueba:

```json
{
  "status": "completed",
  "data": {
    "total": 1,
    "amountTotal": 10000.13,
    "currency": "ARS",
    "items": [
      {
        "period": "2026/1",
        "description": "Impuesto automotor",
        "dueDate": "2026-06-13",
        "amount": 9000,
        "updatedAmount": 10000.13,
        "status": "with_debt",
        "jurisdiction": "Pergamino"
      }
    ]
  },
  "error": null
}
```

Ejemplo de consulta completada sin registros:

```json
{
  "status": "completed",
  "data": {
    "total": 0,
    "amountTotal": 0,
    "currency": "ARS",
    "items": []
  },
  "error": null
}
```

Para ver el schema completo y todos los códigos de respuesta, consultá la referencia de API.

- [Operaciones →](https://clasific.ar/docs/v2/operations)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## GNC

Documentación: https://clasific.ar/docs/v2/modules/cng

### Qué devuelve

El módulo GNC consulta los registros disponibles asociados al equipo de GNC del vehículo.

### Consultar GNC

Usá gnc en `POST /v2/vehicles/:plate/modules` para consultar GNC.

```bash
curl -X POST 'https://api.clasific.ar/v2/vehicles/ZZ000ZZ/modules' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"modules":["gnc"]}'
```

Cada solicitud nueva consume 1 consulta de módulo.

- [Cómo consultar y combinar módulos →](https://clasific.ar/docs/v2/modules)

### Campos del resultado

Estos campos están dentro de data. Los campos marcados con null pueden no tener información disponible. Las fechas se devuelven como fecha o fecha y hora ISO; si no se pueden interpretar, su valor es null.

| Campo | Tipo | Descripción |
| --- | --- | --- |
| total | number | Cantidad de registros en items. |
| items | array | Listado de registros. |
| items[].operationDate | string \| null | Fecha de la operación registrada. |
| items[].workshop | string \| null | Nombre o identificación del taller informado. |
| items[].workshopTaxId | string \| null | Identificación tributaria del taller, cuando está disponible. |
| items[].workshopCode | string \| null | Código del taller informado. |
| items[].sticker | string \| null | Identificador de oblea informado para el registro. |
| items[].previousSticker | string \| null | Identificador de la oblea anterior, cuando está disponible. |

### Cobertura y estados

La disponibilidad depende de la información provista por el sistema consultado al momento de la operación.

completed con total: 0 significa que la consulta se completó correctamente sin encontrar registros en las fuentes consultadas. No es un error.

Un resultado sin registros no constituye una certificación del estado actual del equipo.

Si no fue posible completar el módulo, el resultado indica failed e incluye un error.

- [Errores →](https://clasific.ar/docs/v2/errors)

### Ejemplo de respuesta

Este es el resultado de modules.gnc dentro de la operación.

Ejemplo de consulta completada sin registros:

```json
{
  "status": "completed",
  "data": {
    "total": 0,
    "items": []
  },
  "error": null
}
```

Para ver el schema completo y todos los códigos de respuesta, consultá la referencia de API.

- [Operaciones →](https://clasific.ar/docs/v2/operations)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Operaciones

Documentación: https://clasific.ar/docs/v2/operations

### Qué es una operación

Algunas consultas se procesan de forma asincrónica. Cuando eso ocurre, Clasificar devuelve un ID de operación que podés guardar para seguir su estado y obtener el resultado final.

Consultá `GET /v2/operations/:id` con el ID recibido:

```bash
curl 'https://api.clasific.ar/v2/operations/op_92cff723-014a-4b1a-aa26-82bb5287e6aa' \
  -H 'x-api-key: <TU_API_KEY>'
```

Consultar una operación no consume consultas de patente ni consultas de módulos. Sí está sujeto al límite de requests por minuto de tu cuenta.

Sólo podés consultar operaciones de tu propia cuenta.

- [Errores →](https://clasific.ar/docs/v2/errors)

### Tipos de operación

kind indica qué consulta dio origen a la operación y dónde leer su resultado.

| kind | Origen | Resultado |
| --- | --- | --- |
| vehicle | Una búsqueda de vehículo que necesitó procesamiento adicional. | result |
| modules | Una consulta de uno o varios módulos. | modules |

La respuesta incluye id, kind, status, plate, createdAt y updatedAt. Las fechas indican cuándo se creó y cuándo se actualizó la operación.

### Estados

| Estado | Significado |
| --- | --- |
| queued | La operación fue aceptada y está esperando comenzar. |
| processing | La operación está siendo procesada. |
| completed | El procesamiento terminó correctamente. |
| partial | El procesamiento terminó con resultados parciales o cobertura reducida. |
| failed | No fue posible completar la operación. |

completed, partial y failed son estados finales. Cuando una operación alcanza uno de ellos, podés dejar de consultar.

Un 200 OK al consultar una operación significa que pudimos recuperar su estado. La operación dentro de la respuesta puede igualmente haber terminado como failed.

### Resultado de módulos

En una operación modular, modules contiene el estado y resultado de cada módulo solicitado. Revisá status, data y error de cada uno; también puede incluir warnings.

La operación termina como partial si algún módulo tuvo cobertura reducida o si hubo módulos que fallaron y otros que pudieron responder. Si todos fallaron, termina como failed. Un resultado sin registros puede ser una consulta completada correctamente.

```json
{
  "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa",
  "kind": "modules",
  "status": "completed",
  "plate": "ZZ000ZZ",
  "modules": {
    "gnc": {
      "status": "completed",
      "data": {
        "total": 0,
        "items": []
      },
      "error": null
    }
  },
  "createdAt": "2026-09-06T12:00:00.000Z",
  "updatedAt": "2026-09-06T12:00:00.000Z"
}
```

Para ver todos los campos y códigos de respuesta, consultá la referencia de API.

- [Polling →](https://clasific.ar/docs/v2/polling)
- [Webhooks →](https://clasific.ar/docs/v2/webhooks)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Polling

Documentación: https://clasific.ar/docs/v2/polling

### Consultar una operación

Usá el ID recibido al iniciar una operación para consultar `GET /v2/operations/:id`:

```bash
curl 'https://api.clasific.ar/v2/operations/op_92cff723-014a-4b1a-aa26-82bb5287e6aa' \
  -H 'x-api-key: <TU_API_KEY>'
```

Mientras el estado sea queued o processing, la operación sigue en curso. Podés dejar de consultar cuando llegue a completed, partial o failed.

- Iniciás una consulta y guardás el ID de operación recibido.
- Consultás la operación con ese ID.
- Si sigue en curso, esperás y volvés a consultar.
- Cuando termina, revisás su estado y usás el resultado disponible.

- [Estados de operación →](https://clasific.ar/docs/v2/operations#states)

### Consumo

El polling no consume consultas de patente ni consultas de módulos.

Cada request sí cuenta dentro del límite de requests por minuto de tu plan.

### Frecuencia de polling

No es necesario consultar continuamente. Dejá unos segundos entre requests y adaptá la frecuencia al tiempo de respuesta de tu integración.

Si recibís 429 Too Many Requests por alcanzar el límite de requests, respetá Retry-After antes de volver a intentar. El header indica cuántos segundos esperar.

- [Rate limits →](https://clasific.ar/docs/v2/rate-limits)

### Usar webhooks

También podés recibir el resultado mediante webhook, sin hacer polling.

- [Webhooks →](https://clasific.ar/docs/v2/webhooks)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Webhooks

Documentación: https://clasific.ar/docs/v2/webhooks

### Recibir resultados automáticamente

En lugar de consultar una operación mediante polling, podés configurar una URL para que Clasificar te envíe una notificación cuando el resultado esté disponible.

V1, V2 y Cuenta → Webhook comparten una configuración por cuenta y ambiente, con destinos independientes para Sandbox y Producción. Habilitá el webhook antes de que termine la operación que querés recibir.

### Configurar un webhook

Usá `GET /v2/webhooks/config` para consultar la configuración y `PATCH /v2/webhooks/config` para definir o actualizar el destino. La configuración también se administra desde `/v1/webhooks/config` o Cuenta → Webhook; cambiar la URL, el secreto o la activación afecta a ambas versiones en ese ambiente. La URL debe ser HTTPS y accesible públicamente.

| Campo | Tipo | Descripción |
| --- | --- | --- |
| url | string \| null | URL de destino, hasta 2048 caracteres. Usá null para quitarla, junto con enabled: false si estaba habilitada. |
| enabled | boolean | Habilita o deshabilita el webhook. Para habilitarlo necesitás una URL. |
| rotateSecret | boolean | Con true genera un nuevo secreto de firma. |

```bash
curl -X PATCH 'https://api.clasific.ar/v2/webhooks/config' \
  -H 'x-api-key: <TU_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://tu-dominio.example/webhooks/clasificar","enabled":true}'
```

Todos los campos del PATCH son opcionales. Los valores omitidos se conservan.

GET y PATCH devuelven url, enabled y secret. El secreto está disponible cada vez que consultás la configuración; no se muestra sólo al crearlo.

Usá secret para verificar las firmas. Guardalo de forma segura en tu servidor y no lo expongas en el frontend. Si lo rotás, las entregas posteriores se firman con el nuevo secreto, incluidos los reintentos.

### Eventos

Las operaciones asincrónicas generan eventos cuando terminan. Las consultas de vehículo que se resuelven de forma inmediata no generan estos eventos.

| Evento | Cuándo se envía |
| --- | --- |
| modules.completed | Una operación de módulos termina como completed o partial. |
| modules.failed | Una operación de módulos termina como failed. |
| vehicle.completed | Una búsqueda asincrónica de vehículo termina correctamente. |
| vehicle.failed | Una búsqueda asincrónica de vehículo termina como failed. |
| webhook.test | Solicitás un evento de prueba. |

Una operación modular con estado partial llega mediante modules.completed. Revisá siempre data.status y el estado de cada módulo.

### Payload

| Campo | Descripción |
| --- | --- |
| id | ID del evento. Usalo para reconocer duplicados. |
| type | Tipo de evento, por ejemplo modules.completed. |
| apiVersion | Versión de la API: v2. |
| createdAt | Fecha y hora del evento en formato ISO. |
| data | En eventos de operación, la misma representación de la operación que obtenés mediante polling. En webhook.test contiene un mensaje de prueba. |

```json
{
  "id": "evt_7c62755f-6270-47b1-9c08-503e3e44bcf4",
  "type": "modules.completed",
  "apiVersion": "v2",
  "createdAt": "2026-09-06T12:00:00.000Z",
  "data": {
    "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa",
    "kind": "modules",
    "status": "completed",
    "plate": "ZZ000ZZ",
    "modules": {
      "gnc": {
        "status": "completed",
        "data": {
          "total": 0,
          "items": []
        },
        "error": null
      }
    },
    "createdAt": "2026-09-06T12:00:00.000Z",
    "updatedAt": "2026-09-06T12:00:00.000Z"
  }
}
```

### Reintentos y duplicados

Las entregas pueden repetirse (at-least-once). En algunos casos podés recibir el mismo evento más de una vez: usá payload.id para deduplicarlo y evitar procesarlo de nuevo.

Respondé con cualquier status HTTP 2xx para confirmar la entrega.

Si tu endpoint no responde correctamente, Clasificar vuelve a intentar hasta un máximo de 5 intentos en total, incluido el primero. Al agotar los intentos, la entrega queda como failed.

### Verificar la firma

Verificá la firma antes de procesar cada webhook. Usá el secreto de la configuración y estos headers:

| Header | Descripción |
| --- | --- |
| x-clasificar-signature | Firma HMAC-SHA256, con formato v1=<hex>. |
| x-clasificar-timestamp | Timestamp Unix en segundos, enviado como texto y utilizado para calcular la firma. |
| x-clasificar-event | Tipo de evento con prefijo v2., por ejemplo v2.modules.completed. En payload.type el valor es modules.completed. |
| x-clasificar-delivery | ID de la entrega, sin el prefijo evt_ del ID de evento. |

La firma se calcula sobre el timestamp, un punto y el body exacto recibido, codificados en UTF-8. No vuelvas a serializar el JSON antes de verificarlo.

```text
signedPayload = timestamp + "." + rawBody
hex = HMAC-SHA256(secret, signedPayload).hex
expectedSignature = "v1=" + hex
```

Compará expectedSignature con x-clasificar-signature usando una comparación de tiempo constante. Usá el texto de x-clasificar-timestamp tal como lo recibiste.

El prefijo v1= identifica la versión del formato de firma, no la versión de la API. El payload sigue teniendo apiVersion: "v2".

### Historial de entregas

Consultá `GET /v2/webhooks/deliveries` para revisar entregas recientes de V1 y V2 en el ambiente actual y diagnosticar problemas. La respuesta contiene deliveries, limit y offset.

| Parámetro | Valores | Por defecto |
| --- | --- | --- |
| limit | Entero de 1 a 100 | 25 |
| offset | Entero de 0 a 10000 | 0 |

| Campo de entrega | Descripción |
| --- | --- |
| id | Identificador de la entrega. |
| event | Tipo de evento. Los eventos V2 se muestran sin el prefijo v2. del header. |
| apiVersion | Versión que originó el evento: v1 o v2. El payload conserva el formato de esa versión. |
| status | pending: pendiente o esperando reintento; processed: confirmada con 2xx; failed: agotó los intentos. |
| attempts | Cantidad de intentos realizados. |
| responseStatus | Último código HTTP recibido; puede ser null si no hubo respuesta. |
| nextAttemptAt | Fecha prevista para el próximo intento. Revisá status para saber si sigue pendiente. |
| createdAt | Fecha de creación de la entrega. |
| payload | Contenido del evento enviado. |

El estado de una entrega indica si tu endpoint la recibió; no es el estado de la operación.

### Probar la configuración

Usá `POST /v2/webhooks/test` para enviar un evento webhook.test a la URL configurada. Requiere una URL y el webhook habilitado; no necesita body.

```bash
curl -X POST 'https://api.clasific.ar/v2/webhooks/test' \
  -H 'x-api-key: <TU_API_KEY>'
```

Recibís HTTP 202 con la entrega pendiente:

```json
{
  "id": "7c62755f-6270-47b1-9c08-503e3e44bcf4",
  "status": "pending"
}
```

Consultá el historial para comprobar si tu endpoint confirmó la recepción.

Configurar, consultar y probar webhooks no consume consultas de patente ni de módulos. Estas requests sí están sujetas al rate limit de tu cuenta.

- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Precios y consumo

Documentación: https://clasific.ar/docs/v2/usage

### Cómo se cuenta el uso

Tu plan tiene dos cupos independientes: consultas de patente y consultas de módulos.

#### Consultas de patente

Cada request aceptado a `GET /v2/vehicles/:plate` consume 1 consulta de patente.

```text
GET /v2/vehicles/ZZ000ZZ
→ 1 consulta de patente
```

#### Consultas de módulos

Cada módulo solicitado en una nueva consulta a `POST /v2/vehicles/:plate/modules` consume 1 consulta de módulo. Consultar módulos no consume consultas de patente.

```text
POST /v2/vehicles/ZZ000ZZ/modules
{"modules":["fines","vtv","tax_debt"]}
→ 3 consultas de módulos
```

Total de esta integración: 1 consulta de patente y 3 consultas de módulos.

Si repetís exactamente la misma consulta de módulos mientras la operación sigue en curso, Clasificar devuelve la operación existente y no consume nuevamente. Cuando la operación ya terminó, una nueva solicitud vuelve a consumir el cupo correspondiente.

Una consulta aceptada consume normalmente aunque no encontremos registros: found: false sigue siendo una consulta de patente, y un módulo completado con total: 0 sigue siendo una consulta de módulo.

Los requests rechazados antes de ser aceptados, por ejemplo por una patente inválida o falta de cupo, no consumen consultas de patente ni de módulos.

Consultar `GET /v2/operations/:id` no consume consultas de patente ni de módulos. Sí cuenta dentro del límite de requests por minuto.

- [Consultar módulos →](https://clasific.ar/docs/v2/modules)
- [Polling →](https://clasific.ar/docs/v2/polling)
- [Errores →](https://clasific.ar/docs/v2/errors)

### Planes

| Plan | Consultas de patente | Consultas de módulos | RPM |
| --- | --- | --- | --- |
| Free | 200 / mes | 10 / mes | 30 |
| Basic | 2.500 / mes | 50 / mes | 60 |
| Growth | 10.000 / mes | 100 / mes | 120 |
| Scale | Sin cupo mensual* | 500 / mes | 240 |

*Scale permite hasta 1.000 patentes distintas por día.

¿Necesitás más volumen? Los planes Custom permiten límites adaptados a tu integración.

- [Ver precios completos →](https://clasific.ar/precios-api)

### Scale

Scale no tiene un límite mensual de consultas de patente. En cambio, admite hasta 1.000 patentes distintas por día.

Consultar ZZ000ZZ varias veces durante el mismo día cuenta como una sola patente distinta para ese límite diario.

Después de consultar 1.000 patentes distintas, podés seguir consultando cualquiera de esas mismas patentes durante el día. Una patente nueva adicional se rechaza hasta el siguiente período diario.

El límite diario se reinicia cada día según la hora de Argentina. El límite de requests por minuto sigue aplicando.

### Consultar tu uso

Usá `GET /v2/usage` para revisar tu plan, las consultas usadas y disponibles, las patentes distintas del día cuando aplica, el límite de requests por minuto y las fechas de reinicio.

```bash
curl 'https://api.clasific.ar/v2/usage' \
  -H 'x-api-key: <TU_API_KEY>'
```

Ejemplo abreviado de una respuesta Free: se muestran los cupos y entitlements.plan. El endpoint incluye campos adicionales.

```json
{
  "entitlements": {
    "plan": "free"
  },
  "plateQueries": {
    "limit": 200,
    "used": 1,
    "remaining": 199,
    "startsAt": "2026-09-01T03:00:00.000Z",
    "endsAt": "2026-10-01T03:00:00.000Z"
  },
  "moduleQueries": {
    "limit": 10,
    "used": 3,
    "remaining": 7,
    "startsAt": "2026-09-01T03:00:00.000Z",
    "endsAt": "2026-10-01T03:00:00.000Z"
  },
  "uniquePlates": {
    "limit": null,
    "used": 1,
    "remaining": null,
    "startsAt": "2026-09-06T03:00:00.000Z",
    "endsAt": "2026-09-07T03:00:00.000Z"
  },
  "requests": {
    "limit": 30,
    "used": 3,
    "remaining": 27,
    "startsAt": "2026-09-06T12:00:00.000Z",
    "endsAt": "2026-09-06T12:01:00.000Z"
  }
}
```

Cada grupo incluye limit, used, remaining, startsAt y endsAt. En limit y remaining, null indica que ese límite no aplica al plan.

- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Rate limits

Documentación: https://clasific.ar/docs/v2/rate-limits

### Requests por minuto

Cada plan define una cantidad máxima de requests por minuto. El límite se comparte entre todas las API keys de tu cuenta: crear varias keys no multiplica los requests disponibles.

Cuentan las consultas de vehículo, el inicio de módulos, el polling, las consultas de uso y la administración de webhooks.

| Plan | Requests por minuto |
| --- | --- |
| Free | 30 |
| Basic | 60 |
| Growth | 120 |
| Scale | 240 |

- [Ver precios y consumo →](https://clasific.ar/docs/v2/usage)

### Headers

En las respuestas que pasan la comprobación del límite podés leer estos headers:

| Header | Descripción |
| --- | --- |
| X-RateLimit-Limit | Cantidad máxima de requests permitidos en la ventana actual. |
| X-RateLimit-Remaining | Requests restantes en la ventana actual, después del request realizado. |
| X-RateLimit-Reset | Momento en que se reinicia la ventana: timestamp Unix, en segundos. |

Si tu plan Custom no tiene límite de RPM, se omiten X-RateLimit-Limit y X-RateLimit-Remaining.

### Cuando alcanzás el límite

Cuando alcanzás el límite, la API responde 429 Too Many Requests con error.code: rate_limit_exceeded.

Retry-After indica cuántos segundos esperar antes de volver a intentar. El error también incluye details.resetAt con la fecha y hora de reinicio.

- Respetá Retry-After antes de reintentar.
- Reducí la frecuencia de requests y evitá polling innecesariamente frecuente.
- Usá backoff: aumentá la espera entre reintentos si el problema continúa.

- [Cómo manejar errores →](https://clasific.ar/docs/v2/errors)

### Rate limit y consumo

El rate limit mide tráfico HTTP; es independiente del consumo de consultas.

Hacer polling con `GET /v2/operations/:id` no consume consultas de patente ni de módulos, pero sí cuenta dentro del rate limit.

Una request autenticada puede contar para el rate limit aunque luego sea rechazada por validación.

- [Precios y consumo →](https://clasific.ar/docs/v2/usage)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## Errores

Documentación: https://clasific.ar/docs/v2/errors

### Formato de error

Los errores HTTP usan una estructura consistente. Usá error.code para manejarlos programáticamente; message está pensado para lectura humana.

| Campo | Descripción |
| --- | --- |
| code | Código estable para manejar el error desde tu integración. |
| message | Descripción legible del error. |
| requestId | Identificador de la request, útil para soporte y diagnóstico. |
| retryable | Indica si el error puede ser transitorio y tiene sentido volver a intentar. |
| details | Información adicional específica del error, cuando está disponible. |

```json
{
  "error": {
    "code": "quota_exceeded",
    "message": "Account limit exceeded.",
    "requestId": "req_example",
    "retryable": true,
    "details": {
      "meter": "module_queries",
      "limit": 10,
      "used": 10,
      "resetAt": "2026-10-01T03:00:00.000Z"
    }
  }
}
```

Guardá requestId cuando reportes un problema a soporte: permite identificar la request correspondiente.

retryable: true no significa que debas repetir inmediatamente una consulta. Cuando estén disponibles, respetá Retry-After y details.resetAt. En quota_exceeded, esperá al reinicio del cupo o revisá los límites de tu plan.

Para quota_exceeded, details.meter identifica el cupo: plate_queries, module_queries o unique_plates. En rate_limit_exceeded, el valor es requests.

### Errores comunes

| Código | HTTP | Qué hacer |
| --- | --- | --- |
| invalid_api_key | 401 | Verificá que estés enviando una API key activa. |
| access_denied | 403 | Tu cuenta no tiene acceso a esa operación o capacidad. |
| invalid_plate | 400 | Verificá el formato de la patente: ABC123 o AB123CD. |
| invalid_request | 400 / 404 | Revisá los parámetros, el body y la ruta solicitada. |
| unsupported_module | 400 | Usá fines, vtv, tax_debt o gnc. |
| duplicate_module | 400 | No repitas el mismo módulo en una solicitud. |
| module_not_enabled | 403 | El módulo no está habilitado para tu cuenta. |
| quota_exceeded | 429 | Alcanzaste el cupo disponible. Revisá el límite y su fecha de reinicio. |
| rate_limit_exceeded | 429 | Esperá el tiempo indicado por Retry-After. |
| operation_not_found | 404 | Verificá el ID de la operación. |
| catalog_not_found | 404 | Usá un ID devuelto por el catálogo del ambiente consultado. |
| temporarily_unavailable | 503 | Esperá y volvé a intentar si el error lo indica. |
| internal_error | 500 | Guardá requestId y revisá retryable antes de reintentar. |

### Consumo y errores

Los requests rechazados antes de ser aceptados no consumen consultas de patente ni de módulos. Por ejemplo: una patente inválida, un módulo no soportado o falta de cupo.

Una operación ya aceptada puede terminar posteriormente con un error de módulo. En ese caso, el resultado muestra el error dentro del módulo correspondiente.

- [Precios y consumo →](https://clasific.ar/docs/v2/usage)

### Errores durante una operación

Una consulta modular puede ser aceptada correctamente y, más tarde, uno de sus módulos puede terminar como failed.

`GET /v2/operations/:id` puede devolver 200 OK porque pudimos recuperar su estado, mientras el resultado de un módulo contiene:

```json
{
  "status": "failed",
  "data": null,
  "error": {
    "code": "module_unavailable",
    "message": "No se pudo completar el módulo.",
    "retryable": true
  }
}
```

Un módulo con status: completed y total: 0 se procesó correctamente y no encontró registros. No es lo mismo que status: failed.

- [Operaciones →](https://clasific.ar/docs/v2/operations)
- [Rate limits →](https://clasific.ar/docs/v2/rate-limits)
- [Referencia de API →](https://api.clasific.ar/v2/openapi.json)

## API reference

Documentación: https://clasific.ar/docs/v2/reference

### Rutas V2

Todas las rutas autenticadas suman 1 al RPM. Basic y Modules tienen consumo independiente. No existe `POST /v2/lookup`.

| Método | Path | Consumo | HTTP exitoso |
| --- | --- | --- | --- |
| GET | `/v2/catalog/brands` | Sin consumo; sólo RPM | 200 |
| GET | `/v2/catalog/models` | Sin consumo; sólo RPM | 200 |
| GET | `/v2/catalog/versions` | Sin consumo; sólo RPM | 200 |
| GET | `/v2/catalog/years` | Sin consumo; sólo RPM | 200 |
| GET | `/v2/vehicles/:plate` | 1 plate query | 200 o 202 |
| POST | `/v2/vehicles/:plate/modules` | N module queries; 0 si reutiliza activo | 202 nueva / 200 reutilizada |
| GET | `/v2/operations/:id` | 0 unidades comerciales | 200 |
| GET | `/v2/status` | 0 unidades comerciales | 200 |
| GET | `/v2/usage` | 0 unidades comerciales | 200 |
| GET | `/v2/plans` | Pública; 0 RPM | 200 |
| GET | `/v2/openapi.json` | Pública; 0 RPM | 200 |
| GET | `/v2/webhooks/config` | 0 unidades comerciales | 200 |
| PATCH | `/v2/webhooks/config` | 0 unidades comerciales | 200 |
| GET | `/v2/webhooks/deliveries` | 0 unidades comerciales | 200 |
| POST | `/v2/webhooks/test` | 0 unidades comerciales | 202 |

- [OpenAPI V2 completo](https://api.clasific.ar/v2/openapi.json)

### Disponibilidad y convivencia con V1

API V2 es la versión actual de Clasificar. Autenticá tus consultas con una API key y consultá los permisos y cupos de tu cuenta en el endpoint de usage.

V1 conserva sus rutas, IDs, cuotas, estados e idempotencia. Consultá la documentación V1 para mantener integraciones de esa versión.

- [Referencia V1](https://clasific.ar/docs/reference)

## Clasificar MCP

Documentación: https://clasific.ar/docs/v2/mcp

Datos del vehículo, multas, deuda de patente, VTV/RTO y GNC, según los permisos de tu cuenta.

### Integrá Clasificar MCP

Seguí la guía de tu agente y autorizá el acceso con tu cuenta de Clasificar.

#### Codex

#### Codex CLI

Agregá el servidor desde tu terminal:

```bash
codex mcp add clasificar --url https://api.clasific.ar/mcp
```

Si no se inició la autorización al agregarlo, ejecutá:

```bash
codex mcp login clasificar
```

Después de autorizar, abrí una nueva sesión de Codex y usá `/mcp` para revisar Clasificar y sus herramientas. Guardar la configuración no confirma la conexión.

- [Documentación de Codex](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)

#### OpenCode

#### OpenCode 2

En la carpeta del proyecto donde vas a usarlo, agregá el servidor e iniciá la autorización:

```bash
opencode mcp add clasificar --url https://api.clasific.ar/mcp
opencode mcp auth clasificar
```

Después de autorizar, ejecutá `opencode mcp list` y comprobá el estado de Clasificar. También podés abrir `/mcps` dentro de OpenCode.

- [Documentación de OpenCode 2](https://opencode.ai/v2/docs/mcp-servers)
- [¿Usás OpenCode 1.x? Seguí la configuración de esa versión](https://opencode.ai/docs/mcp-servers/)

#### Claude Code

#### Claude Code

Ejecutá este comando en la carpeta de tu proyecto:

```bash
claude mcp add --transport http clasificar https://api.clasific.ar/mcp
```

Abrí Claude Code, escribí `/mcp`, elegí Clasificar e iniciá la autorización en el navegador. Al volver, revisá el estado del servidor y sus herramientas en ese mismo panel. Esta guía corresponde a Claude Code, no a la app web de Claude.

- [Documentación de Claude Code](https://code.claude.com/docs/en/mcp)

#### Kimi Code

#### Kimi Code CLI

Agregá el servidor con OAuth e iniciá la autorización:

```bash
kimi mcp add --transport http --auth oauth clasificar https://api.clasific.ar/mcp
kimi mcp auth clasificar
```

Después de autorizar, comprobá la conexión y listá las herramientas:

```bash
kimi mcp test clasificar
```

Esta prueba lista herramientas; no ejecuta consultas vehiculares. La guía corresponde a Kimi Code CLI, no al chat web de Kimi.

- [Documentación de Kimi Code CLI](https://moonshotai.github.io/kimi-cli/en/reference/kimi-mcp.html)

#### Otro cliente

#### Configurá un cliente MCP remoto

Agregá esta dirección en la configuración de servidores MCP de tu aplicación:

```text
https://api.clasific.ar/mcp
```

Elegí Streamable HTTP y OAuth. El cliente necesita PKCE y registro dinámico (DCR); si no admite ese flujo, no puede usar esta conexión. Después de autorizar, listá las herramientas para comprobar el acceso.

- [Requisitos de compatibilidad y cliente TypeScript](https://clasific.ar/docs/v2/mcp/integration#compatibility)

#### Autorizá tu cuenta en Clasificar

Cuando se abra el navegador, ingresá a Clasificar, revisá la cuenta y los permisos, y elegí Autorizar acceso. Si te registrás, primero verificá tu email. No pegues contraseñas ni tokens en la conversación.

Conectar y listar herramientas no consume unidades de consulta. Las consultas vehiculares sí usan el cupo de tu cuenta: no hay sandbox MCP.

- [Revisar disponibilidad en Asistentes IA](https://clasific.ar/account/mcp)

Guías contrastadas con la documentación oficial. La conexión completa con estos clientes todavía está pendiente de validación.

- [Ver compatibilidad y alcance de la verificación](https://clasific.ar/docs/v2/mcp/integration#compatibility)

#### También podés pedirle ayuda a tu agente

Copiá las instrucciones y pegáselas a tu agente. Si puede configurar servidores MCP, te ayudará a iniciar la conexión.

```text
Ayudame a conectar Clasificar MCP: https://api.clasific.ar/mcp. Guía: https://clasific.ar/docs/v2/mcp.

Usá Streamable HTTP y OAuth con PKCE. Si tu aplicación lo admite, iniciá la conexión y dejame revisar y autorizar el acceso en Clasificar. Si requiere configuración manual o no es compatible, indicámelo sin simular una conexión exitosa. No me pidas contraseñas ni tokens en el chat.

Comprobá el acceso listando herramientas, sin ejecutar consultas vehiculares. Antes de una consulta que consuma cupo, explicame el consumo y pedime confirmación.
```

### Probá una consulta

Pedile una consulta concreta al agente. Reemplazá la patente ficticia del ejemplo por la que querés consultar:

```text
¿Qué marca, modelo y año figuran para AB123CD?
```

El agente debe avisarte que utilizará 1 consulta de patente y pedir tu confirmación. Después usa `consultar_vehiculo` y presenta los datos disponibles. La consulta consume esa unidad incluso si no encuentra registros.

Un resultado vacío no garantiza ausencia de multas, deuda o antecedentes. MCP no ofrece datos del titular; el Informe de Dominio tiene un flujo independiente.

- [Ver herramientas, argumentos y respuestas](https://clasific.ar/docs/v2/mcp/tools)

### Revisá tus consumos

MCP usa los permisos, cupos y reglas de consumo de la cuenta que autorizaste, compartidos con la API. No tiene un saldo ni una suscripción separados.

Los cupos expresan unidades, no precios. Consultá las condiciones de tu plan; las estimaciones de facturación se muestran por separado de los pagos confirmados.

- Datos del vehículo: 1 consulta de patente. Módulos: 1 consulta por módulo solicitado; reutilizar una operación idéntica en curso no descuenta unidades adicionales.
- Listar herramientas, seguir una operación y revisar el consumo no consume unidades de consulta; sí aplica el límite de solicitudes por minuto.
- En Asistentes IA podés ver los límites y el uso de API y MCP, y revocar accesos en Aplicaciones autorizadas. Cerrar sesión en el sitio no revoca el acceso del agente.

- [Revisar consumos y accesos](https://clasific.ar/account/mcp)
- [Planes y precios](https://clasific.ar/precios-api)
- [Reglas de consumo por herramienta](https://clasific.ar/docs/v2/mcp/tools#consumption)
- [Desconectar una aplicación](https://clasific.ar/docs/v2/mcp/authentication#revocation)

### ¿No podés conectarte?

#### La autorización venció o fue revocada

Iniciá la conexión otra vez desde tu cliente. No reutilices un enlace de autorización anterior.

#### El cliente no ofrece conexión OAuth

Revisá los requisitos de compatibilidad. Si te pide pegar un secreto en el chat, no continúes por ese camino.

#### Conecta, pero no puede consultar

Revisá disponibilidad, permisos y cupo en Asistentes IA. Si alcanzaste el límite de solicitudes por minuto, esperá antes de reintentar.

- [Resolver otros problemas](https://clasific.ar/docs/v2/mcp/troubleshooting)
- [Contactar a soporte](mailto:contacto@clasific.ar)

## Herramientas de MCP

Documentación: https://clasific.ar/docs/v2/mcp/tools

Las cuatro herramientas disponibles comparten los permisos y cupos de tu cuenta. Obtené sus schemas vigentes con `tools/list`. Los argumentos y extractos siguientes son ficticios; no ejecutan consultas.

Cada respuesta contiene JSON dentro de `content[].text`. Comprobá `isError` antes de interpretarla como exitosa; no asumas que hay `structuredContent`. Los extractos omiten campos como fechas para facilitar la lectura.

### Identificá un vehículo

Usá `consultar_vehiculo` para obtener los datos básicos disponibles por patente. Requiere `mcp:query` y autorización previa para consumir 1 consulta de patente, incluso si no devuelve datos o los obtiene de caché.

| Argumento | Valor |
| --- | --- |
| `patente` | Texto, hasta 10 caracteres; formato ABC123 o AB123CD. |
| `confirmar_consumo` | Obligatorio, debe ser `true`. |

#### Ejemplo de argumentos

```json
{
  "patente": "AB123CD",
  "confirmar_consumo": true
}
```

#### Respuesta

Devuelve `operation` y `consumption`. Los datos del vehículo están en `operation.result`. Este extracto ficticio muestra una consulta completada sin registros; también consume una unidad.

```json
{
  "operation": {
    "kind": "vehicle",
    "status": "completed",
    "result": {
      "success": true,
      "found": false,
      "data": null
    }
  },
  "consumption": {
    "plateQueries": 1,
    "moduleQueries": 0
  }
}
```

- [Campos de los datos del vehículo](https://clasific.ar/docs/v2/vehicles)

### Consultá multas, VTV, deuda o GNC

Usá `consultar_modulos` para iniciar una consulta asíncrona. Requiere `mcp:query`; recuperar el resultado requiere también `mcp:read`. Una operación nueva consume una consulta por módulo solicitado.

| Argumento | Valor |
| --- | --- |
| `patente` | Texto en formato ABC123 o AB123CD, hasta 10 caracteres. |
| `modulos` | Entre 1 y 4 valores distintos: fines, vtv, tax_debt, gnc. |
| `confirmar_consumo` | Obligatorio, debe ser `true`. |

#### Módulos disponibles

Podés solicitar de uno a cuatro módulos distintos. La disponibilidad para tu cuenta depende de tu plan.

| ID en la llamada | Información |
| --- | --- |
| [fines](https://clasific.ar/docs/v2/modules/tickets) | Multas |
| [vtv](https://clasific.ar/docs/v2/modules/vtv) | VTV / RTO |
| [tax_debt](https://clasific.ar/docs/v2/modules/tax-debt) | Deuda de patente |
| [gnc](https://clasific.ar/docs/v2/modules/cng) | GNC |

#### Ejemplo de argumentos

```json
{
  "patente": "AB123CD",
  "modulos": [
    "vtv"
  ],
  "confirmar_consumo": true
}
```

#### Respuesta

Devuelve `operation`, `reused` y `consumption`. Guardá `operation.id` y seguí con `consultar_operacion`. Extracto ficticio de una operación nueva:

```json
{
  "operation": {
    "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa",
    "kind": "modules",
    "status": "queued",
    "plate": "AB123CD",
    "modules": {
      "vtv": {
        "status": "queued",
        "data": null,
        "error": null
      }
    }
  },
  "reused": false,
  "consumption": {
    "plateQueries": 0,
    "moduleQueries": 1
  }
}
```

Una solicitud idéntica en curso se reutiliza sin unidades adicionales, incluso entre API y MCP de la misma cuenta. Volver a solicitarla después de que terminó crea una consulta nueva y vuelve a consumir cupo.

- [Seguir operaciones sin repetir la consulta](https://clasific.ar/docs/v2/mcp/integration#operations)

### Recuperá el resultado de una operación

Usá `consultar_operacion` para recuperar el estado y los resultados de una operación de tu cuenta. Requiere `mcp:read`; no consume unidades de consulta.

```json
{
  "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa"
}
```

El argumento `id` es el `operation.id` devuelto al iniciar la consulta, con formato `op_<uuid>`. La respuesta es la operación directamente, sin otro campo `operation` que la envuelva.

#### Respuesta

Extracto ficticio de la misma operación anterior, ahora completada sin registros de VTV. No implica que el vehículo carezca de revisiones.

```json
{
  "id": "op_92cff723-014a-4b1a-aa26-82bb5287e6aa",
  "kind": "modules",
  "status": "completed",
  "plate": "AB123CD",
  "modules": {
    "vtv": {
      "status": "completed",
      "data": {
        "total": 0,
        "items": []
      },
      "error": null
    }
  }
}
```

- [Estructura y estados de las operaciones](https://clasific.ar/docs/v2/operations)

### Consultá el consumo de tu cuenta

Usá `consultar_consumo` para revisar el plan, permisos, cupos y consumo de la cuenta que autorizaste. Requiere `mcp:read`; no consume unidades de consulta. Enviá un objeto vacío:

```json
{}
```

Devuelve `entitlements`, los cupos `plateQueries`, `moduleQueries`, `uniquePlates` y `requests`, y el desglose `byChannel` entre API y MCP. Los cupos incluyen límite, uso, unidades restantes y período; no son una cotización monetaria.

- [Campos y ejemplo de consumo](https://clasific.ar/docs/v2/usage)
- [Ver tus consumos](https://clasific.ar/account/mcp)

### Cuándo se consume cupo

MCP utiliza los mismos cupos y reglas comerciales que tu API. No tiene una suscripción ni un saldo separado. Consultá el precio y las condiciones de tu plan; las unidades disponibles no son una cotización monetaria.

Por ejemplo, pedir multas y VTV en una nueva operación utiliza 2 consultas de módulos. Consultar además los datos básicos del vehículo utiliza 1 consulta de patente. Listar herramientas, consultar el estado de una operación y revisar el consumo no descuentan esas unidades.

Todas las solicitudes autenticadas al servidor MCP, incluidas inicialización, listado, ping, estado y consumo, cuentan para el límite de solicitudes por minuto compartido. Los endpoints OAuth tienen limitadores propios.

- [Cupos y reglas de consumo de la API](https://clasific.ar/docs/v2/usage)
- [Planes y precios](https://clasific.ar/precios-api)

### Alcance de los resultados

Los datos dependen de las fuentes consultadas y su cobertura. Un resultado vacío indica que no se devolvieron registros; no garantiza ausencia de multas, deudas o antecedentes.

En VTV/RTO, una revisión aprobada, su fecha y el vencimiento informado son datos distintos. No deduzcas vigencia únicamente de una revisión aprobada.

MCP no ofrece información de titulares. Esa información corresponde al Informe de Dominio, un producto con un flujo independiente.

- [Consultar fuentes y cobertura](https://clasific.ar/fuentes)

## Autenticación y permisos de MCP

Documentación: https://clasific.ar/docs/v2/mcp/authentication

Autorizá el acceso con OAuth desde Clasificar. Revisá la cuenta, los permisos, el nombre de la aplicación y el dominio al que vas a volver antes de aprobar. El nombre lo declara la aplicación; no acredita que su marca esté verificada.

### Autorizá una aplicación

Autorizar y listar herramientas no consume unidades de consulta. El listado está sujeto al límite de solicitudes por minuto. No compartas contraseñas ni tokens por chat; el cliente debe almacenarlos fuera del contexto del modelo. La aplicación autorizada puede procesar las consultas y los resultados.

1. Agregá `https://api.clasific.ar/mcp` en tu cliente e iniciá OAuth.
2. Ingresá a Clasificar; si creás una cuenta, verificá tu email. Revisá que sea la cuenta cuyo cupo querés usar.
3. Revisá la solicitud y elegí Autorizar acceso o Cancelar. Volvé al cliente y listá las herramientas.

### Revisá los permisos solicitados

La pantalla de consentimiento muestra los permisos solicitados. El cliente puede pedir estos alcances; si no especifica ninguno, se solicitan ambos. La pantalla permite aceptar o cancelar la solicitud, sin editar permisos individualmente.

| Permiso OAuth | Qué permite |
| --- | --- |
| `mcp:read` | Ver plan, permisos, cupos y consumo mediante `consultar_consumo`, y recuperar estados y resultados de operaciones de la cuenta mediante `consultar_operacion`. |
| `mcp:query` | Iniciar consultas de vehículos y módulos mediante `consultar_vehiculo` y `consultar_modulos`. Estas consultas pueden consumir cupo. |

Los permisos OAuth nunca amplían las capacidades del plan. El backend comprueba el acceso de la cuenta, los módulos habilitados y los cupos. Tener permiso para consultar no garantiza que todos los módulos estén habilitados.

El permiso de lectura de operaciones alcanza a las operaciones de tu cuenta, no solo a las iniciadas por esa aplicación. No permite consultar operaciones de otras cuentas.

Para iniciar una consulta de módulos y recuperar después sus resultados, el cliente necesita ambos permisos: `mcp:query` para iniciarla y `mcp:read` para seguir la operación.

OAuth autoriza el acceso de la aplicación. Antes de una consulta facturable, el asistente debe pedir autorización y enviar `confirmar_consumo: true`. Ese campo es una declaración del cliente: no es una comprobación independiente de una aprobación humana por consulta. Los permisos y los cupos se controlan en el servidor.

### Revocá el acceso

1. Abrí Asistentes IA en tu cuenta de Clasificar.
2. Buscá la autorización en Aplicaciones autorizadas y elegí Revocar acceso.
3. Las próximas solicitudes y renovaciones de esa autorización serán rechazadas. Para volver a usarla, iniciá una nueva conexión desde el asistente.

La revocación afecta a esa autorización completa, no a las demás autorizaciones ni a las llaves API. Si autorizaste varias veces la misma aplicación, revisá cada entrada que quieras revocar. Eliminar la configuración del asistente no garantiza que haya revocado sus tokens en Clasificar.

Revocar no cancela operaciones ya admitidas, no revierte su consumo y no elimina los resultados que la aplicación ya recibió.

| Estado en el panel | Qué significa |
| --- | --- |
| Autorizada · Sin solicitudes registradas | Aceptaste el acceso, pero todavía no se registró una solicitud autenticada que superara los controles de acceso y frecuencia. |
| Autorizada · Acceso utilizado | Se registró al menos una solicitud autenticada. Puede ser solo un listado; no prueba una consulta facturable ni una conexión activa en este momento. |
| Acceso revocado | Esa autorización no permite nuevas solicitudes ni renovaciones. |

- [Gestionar aplicaciones autorizadas](https://clasific.ar/account/mcp)

### Sesión del sitio y acceso del agente

Cerrar sesión en el sitio no revoca el acceso OAuth. Quitar un servidor del asistente tampoco garantiza su revocación: hacelo desde Aplicaciones autorizadas en Clasificar.

Si la solicitud de autorización vence mientras ingresás o verificás el email, reiniciá la conexión desde el cliente. Si ya no puede renovar su acceso, autorizá de nuevo.

- [Plazos y renovación de tokens](https://clasific.ar/docs/v2/mcp/integration#sessions)

## Integración avanzada de MCP

Documentación: https://clasific.ar/docs/v2/mcp/integration

Referencia para desarrollar un cliente o revisar su compatibilidad. Si tu aplicación ya admite MCP remoto con OAuth, seguí el inicio rápido.

- [Conectar un agente](https://clasific.ar/docs/v2/mcp#connection)

### Comprobá la compatibilidad

La conexión está verificada localmente con el cliente TypeScript del SDK oficial de MCP 1.31.x: autorización OAuth, listado de herramientas, consultas y renovación. Esto no confirma compatibilidad con todas las aplicaciones o versiones.

El cliente necesita Streamable HTTP remoto y OAuth Authorization Code con PKCE S256, descubrimiento de metadata y registro dinámico (DCR) como cliente público con `token_endpoint_auth_method: none`. Debe enviar `resource` y gestionar la renovación de tokens.

No se admiten `client_secret`, el flujo `client_credentials` ni registro mediante Client ID Metadata Documents. Para un cliente propio, implementá el proveedor OAuth del SDK; un token fijo no resuelve la autorización ni su renovación.

No hay sandbox MCP. La URL pública utiliza datos reales y el cupo de la cuenta; comprobá la disponibilidad en Asistentes IA antes de integrar.

- [Revisar disponibilidad](https://clasific.ar/account/mcp)
- [Cliente TypeScript del SDK oficial](https://ts.sdk.modelcontextprotocol.io/client)

### Guías de clientes y opciones de Codex

La guía de inicio incluye la configuración de Codex CLI, OpenCode 2, Claude Code y Kimi Code CLI, con enlaces a sus instrucciones oficiales. Esas guías no equivalen a una validación de extremo a extremo con Clasificar; esa comprobación sigue pendiente.

En Codex, la sintaxis también se contrastó con la CLI 0.160.0. Si necesitás elegir el registro dinámico explícitamente, usá el comando siguiente después de agregar el servidor:

```bash
codex mcp login clasificar --oauth-client-registration dcr
```

- [Elegir un cliente y configurar la conexión](https://clasific.ar/docs/v2/mcp#connection)
- [Referencia OAuth de Codex](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)

### Transporte y comprobación del acceso

Usá `https://api.clasific.ar/mcp` como dirección de Streamable HTTP. El transporte no mantiene sesiones: cada solicitud MCP usa POST con respuesta JSON. No hay un stream SSE independiente por GET.

Inicializá el cliente y ejecutá `tools/list` para comprobar la conexión. No uses `consultar_vehiculo` ni `consultar_modulos` para esto: consumen cupo.

Las solicitudes llevan el token de acceso en el encabezado Authorization. Los clientes de navegador requieren un origen permitido por el servicio; consultá a soporte si tu integración necesita ese acceso.

- [Contactar a soporte](mailto:contacto@clasific.ar)

### Seguí operaciones asíncronas

Los módulos se procesan de forma asíncrona. Guardá `operation.id` de la respuesta y usalo como `id` en `consultar_operacion`. Si sigue en curso, esperá antes de volver a consultar el estado. No inicies otra consulta para recuperar un resultado.

Una solicitud de módulos idéntica que sigue en curso se reutiliza dentro de la misma cuenta, incluso entre API y MCP y entre distintas credenciales. El orden de los módulos no cambia el conjunto solicitado. No se descuentan unidades adicionales; la operación conserva el canal que la inició.

Volver a solicitar módulos después de que la operación terminó inicia una consulta nueva y vuelve a consumir unidades. Esta reutilización no exime del consumo de una consulta básica de vehículo.

- [Estados y estructura de las operaciones](https://clasific.ar/docs/v2/operations)

### Implementá el flujo OAuth

Esta referencia es para quien desarrolla un cliente. El usuario conecta desde su asistente y aprueba en Clasificar; no necesita copiar tokens ni construir estas solicitudes.

Usá `https://api.clasific.ar/mcp` como `resource` en producción. Descubrí los endpoints mediante metadata; las rutas de la tabla pertenecen al origen del servidor MCP, no al sitio público. No agregues parámetros a la URL de transporte.

| Endpoint | Función |
| --- | --- |
| `GET /.well-known/oauth-protected-resource/mcp` | Identifica el recurso, los scopes y los servidores de autorización. El desafío 401 del MCP indica esta metadata en `WWW-Authenticate`. |
| `GET /.well-known/oauth-authorization-server` | Publica issuer, endpoints y soporte de PKCE S256. |
| `POST /register` | Recibe JSON y devuelve la identidad del cliente público. Requiere `redirect_uris` y `token_endpoint_auth_method: none`; `client_name` es opcional. |
| `GET /authorize` | Recibe `client_id`, `response_type=code`, retorno registrado, desafío PKCE S256, `resource`, y opcionalmente `scope` y `state`. Abre el consentimiento. |
| `POST /token` | Intercambia código o refresh token mediante formulario URL-encoded. |
| `POST /revoke` | Recibe `client_id` y `token` mediante formulario URL-encoded. Revoca la autorización completa asociada a ese token del cliente. |

#### Registro y retorno

Registrá de una a cinco URLs de retorno. Deben usar HTTPS o HTTP de loopback local, sin usuario, contraseña ni fragmento. El retorno debe coincidir con el registrado; se admite un puerto variable en loopback local. En el intercambio del código, enviá exactamente el retorno usado al autorizar.

El cliente genera y conserva un `code_verifier`, envía su desafío S256 y comprueba el `state` al volver. Si el usuario cancela, el retorno incluye `error=access_denied` y conserva el `state` recibido; no se emite un código.

#### Intercambio de código y renovación

| Solicitud a /token | Campos del formulario |
| --- | --- |
| Código | `grant_type=authorization_code`, `client_id`, `code`, `code_verifier`, `redirect_uri`, `resource`. |
| Renovación | `grant_type=refresh_token`, `client_id`, `refresh_token`, `resource`. Si enviás `scope`, debe conservar exactamente los permisos originales. |

La respuesta contiene `access_token`, `refresh_token`, `token_type: Bearer`, `expires_in: 600` y `scope`. Los tokens solo sirven para el recurso MCP que autorizaste. Para cambiar los permisos, iniciá una autorización nueva.

#### Usar y proteger los tokens

Enviá el token de acceso en `Authorization: Bearer <ACCESS_TOKEN>` en cada solicitud MCP, incluida la inicialización. La cookie del login de Clasificar no autentica esas solicitudes.

El cliente debe guardar tokens y verificador de PKCE en almacenamiento seguro, separados por usuario y autorización. No los incluyas en URLs, conversaciones, analytics ni logs. Los tokens son credenciales y requieren protección.

Para verificar la conexión, inicializá el cliente y ejecutá `tools/list`. No uses una llamada a `consultar_vehiculo` o `consultar_modulos` como prueba: puede consumir cupo.

### Renová los tokens

El cliente administra los tokens fuera de la conversación y renueva el acceso cuando vence. Estos plazos no representan una sesión de chat.

| Elemento | Duración | Qué ocurre al vencer |
| --- | --- | --- |
| Solicitud de autorización | 10 minutos desde su creación. | Volvé a iniciar la conexión desde el asistente. Esto también aplica si el registro o la verificación del email tarda más. |
| Código de autorización | 60 segundos desde la aprobación; un solo uso. | El cliente debe reiniciar la autorización si no lo intercambia a tiempo. |
| Token de acceso | 10 minutos desde su emisión. | El cliente puede obtener otro mediante su token de renovación. |
| Token de renovación (refresh token) | 30 días desde la emisión de cada token. | Si ya no puede renovarse, es necesario autorizar otra vez. |

Cada renovación entrega un nuevo token de acceso y un nuevo refresh token. El cliente debe reemplazar el anterior y coordinar las renovaciones para no utilizarlo dos veces. Cada nuevo refresh token inicia su propio plazo de 30 días.

Reutilizar un refresh token ya consumido mientras aún no venció revoca toda esa autorización, incluidos los tokens emitidos después. Si el intercambio se interrumpe y el cliente perdió la respuesta, debe volver a autorizar en lugar de insistir con el mismo token.

Los tokens también dejan de permitir acceso si se revoca la autorización. La cuenta debe seguir habilitada para consultar y renovar. El vencimiento de un token no borra automáticamente la autorización del panel.

## Solución de problemas de MCP

Documentación: https://clasific.ar/docs/v2/mcp/troubleshooting

Revisá primero la disponibilidad, la autorización y el cupo de la cuenta. Para comprobar el acceso usá el listado de herramientas; no hace falta ejecutar una consulta vehicular.

- [Abrir Asistentes IA](https://clasific.ar/account/mcp)

### Recuperá la conexión

#### La solicitud venció o no vuelve al asistente

Iniciá la conexión otra vez desde el asistente. No reutilices enlaces de consentimiento o códigos anteriores. Si desarrollás el cliente, verificá el retorno registrado, `state`, PKCE y el `resource` de la solicitud.

#### Me pide una API key o un client secret

Ese flujo no corresponde a esta conexión MCP. Usá OAuth con cliente público y PKCE. Si la aplicación no admite ese método, no está verificada para esta integración; no pegues tu API key como alternativa.

### Revisá por qué falla una consulta

#### No puedo ejecutar una consulta

- Revisá que MCP y tu cuenta estén habilitados y que la autorización no esté revocada.
- Comprobá el permiso OAuth de la herramienta, el módulo habilitado y el cupo disponible.
- Si el token venció, dejá que el cliente lo renueve. Si no puede renovarlo, autorizá otra vez.
- Si alcanzaste el límite de solicitudes, esperá antes de reintentar. No inicies otra operación para seguir una que está en curso.
- Si el servicio está temporalmente indisponible, volvé a intentarlo más tarde.

#### El consumo no aparece en mi panel

Comprobá que estés viendo la misma cuenta que autorizaste en OAuth y actualizá el estado. El consumo corresponde a esa cuenta; un listado de herramientas no aumenta las unidades de consulta.

### Interpretá los errores del cliente

Distinguí errores HTTP de autenticación, errores OAuth y errores de herramientas. Un HTTP 200 no significa necesariamente que una consulta haya tenido éxito.

| Respuesta | Cómo actuar |
| --- | --- |
| HTTP 401 / `invalid_token` en MCP | Falta un token válido, venció o fue revocado. Renovalo si corresponde o volvé a autorizar. |
| HTTP 403 en MCP | Revisá acceso de cuenta, bloqueos u origen del cliente. Cambiar de token no amplía el plan. |
| HTTP 429 | Se alcanzó un límite de solicitudes. Reducí la frecuencia y esperá antes de reintentar. |
| HTTP 503 / `mcp_disabled` o `temporarily_unavailable` | El servicio no está disponible. Revisá el estado de MCP en tu cuenta y volvé a intentarlo más tarde. |
| OAuth `invalid_grant` | Código o refresh token no válido, vencido, ya utilizado o perteneciente a otro cliente. Reiniciá la autorización si no puede recuperarse. |
| OAuth `invalid_scope`, `invalid_request` o `invalid_client_metadata` | Corregí scopes, parámetros, recurso o registro; no reintentes el mismo pedido sin cambios. |
| Resultado de herramienta con `isError: true` | Leé `error.code` en el JSON del bloque de texto: puede indicar permisos, módulo deshabilitado, cupo agotado u otro error de la consulta. |

### Pedí ayuda

Si el problema continúa, indicá qué cliente y versión usás, en qué paso falla y el código de error si aparece. No envíes contraseñas, tokens ni resultados con datos personales.

- [Escribir a soporte](mailto:contacto@clasific.ar)
- [Revisar tu cuenta](https://clasific.ar/account/mcp)
## Documentación de la versión anterior (V1)
# Documentación API de Clasificar

Esta es la documentación completa de integración para agentes, LLMs y desarrolladores.

## Introducción

Clasificar API expone datos vehiculares argentinos para consulta de patentes, datos registrales normalizados y reportes inteligentes asincrónicos.

Base URLs:
- Producción: `https://api.clasific.ar`
- Sandbox: `https://sandbox.clasific.ar`

Formatos de patente aceptados:
- `ABC123`: formato viejo, 3 letras + 3 números.
- `AB123CD`: formato Mercosur, 2 letras + 3 números + 2 letras.

Las patentes son case-insensitive. La API normaliza a mayúsculas.

En sandbox se acepta cualquier patente no vacía para facilitar pruebas end-to-end.

Versionado:
- Esta referencia describe V1. API V2 es la versión actual y comparte cuenta, keys y consumo con V1.

## Primeros pasos

1. Crear una cuenta en Clasificar.
2. Activar un plan desde la cuenta.
3. Crear una API key en la sección de llaves API.
4. Enviar requests con el header `x-api-key`.

Límites visibles en la página:
- 200 consultas Basic por mes compartidas entre V1 y V2.
- 10 módulos mensuales disponibles en V2; módulos V1 requiere Custom.
- 30 requests por minuto por cuenta y entorno, entre todas las keys y ambas versiones.

## Precios API

Los cupos reales de los clientes existentes se consultan en Account. Los planes comerciales
se presentan únicamente en `/precios-api`.

Resumen:
- Gratis: activación self-service, sin tarjeta, con cuota para pruebas iniciales.
- Planes pagos: checkout online, mayor volumen mensual y más capacidad por minuto.
- Todos los planes usan la misma autenticación por `x-api-key` y métricas de uso visibles.

## Autenticación

Todos los endpoints privados requieren:

```http
x-api-key: clk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Notas:
- Las API keys tienen prefijo `clk_`.
- La key completa se muestra una sola vez al crearla.
- Externamente se identifican por `prefix` y `last4`.

## Sistema de cuotas

Los cupos Basic y módulos se registran por cuenta, mes y entorno:

- V1 y V2 comparten los mismos contadores entre todas las API keys.
- Rotar keys, cambiar de versión o de plan no reinicia el uso del mes.
- 600 Basic en V1 + 400 en V2 = 1.000 usadas.
- El RPM también se comparte por cuenta y entorno e incluye polling y status.

Consumo:
- Basic autenticado, con o sin datos: una consulta del cupo Basic mensual compartido.
- Módulos Custom: una unidad por módulo aceptado, compartida con V2.
- Reporte inteligente aceptado o servido desde cache: consume `intelligent` + cuota mensual.
- Polling de reporte inteligente: solo analytics, no consume cuota.

Importante:
- Basic autenticado ya no aplica las cuotas diarias legacy `fast` y `miss`.
- Los reportes V1 conservan sus condiciones y su consumo propios.
- `/v1/status` devuelve `sharedUsage`; `/v2/status` expone el mismo consumo en `data.usage`.
- Los contadores compartidos indican sus fechas de reinicio y zona horaria; por defecto, Argentina.

## Sandbox

Sandbox usa el host `https://sandbox.clasific.ar` y el mismo header `x-api-key`. Cualquier API key válida puede probar consultas básicas, reportes y webhooks en sandbox, incluso si el owner está en plan gratuito. Los endpoints de módulos mantienen el requisito de contrato Custom.

Reglas de sandbox:
- No consulta información real ni crea reportes reales en la base productiva.
- Devuelve placeholders determinísticos a partir de `sandbox_seed` cuando se envía.
- La configuración de webhooks de sandbox está separada de producción.
- `POST /v1/reports` queda en estado `processing` durante una demora realista de 1 a 3 minutos antes de completar o fallar.
- El webhook de reporte se dispara al terminar esa demora. `POST /v1/webhooks/test` sigue siendo un test inmediato de conectividad.
- El query param `sandbox_scenario` permite forzar respuestas: `default`, `clean`, `debt`, `partial`, `unavailable`, `failed`.
- `sandbox_delay_ms` existe para pruebas controladas, pero en el sandbox público se normaliza a la ventana realista de demora.

Walkthrough recomendado:

1. Activar el plan gratuito desde `/account/activate` y crear una key en `/account/keys`.
2. Configurar el webhook de sandbox:

```bash
curl -X PATCH "https://sandbox.clasific.ar/v1/webhooks/config" \
  -H "x-api-key: clk_tu_api_key" \
  -H "content-type: application/json" \
  -d '{
    "url": "https://webhook.site/tu-url-unica",
    "enabled": true,
    "rotate_secret": false
  }'
```

3. Enviar un webhook de prueba:

```bash
curl -X POST "https://sandbox.clasific.ar/v1/webhooks/test" \
  -H "x-api-key: clk_tu_api_key"
```

4. Probar consulta básica:

```bash
curl -G "https://sandbox.clasific.ar/v1/vehicles/basic" \
  -H "x-api-key: clk_tu_api_key" \
  --data-urlencode "plate=CLIENTE-123" \
  --data-urlencode "classification=true" \
  --data-urlencode "sandbox_scenario=debt" \
  --data-urlencode "sandbox_seed=demo-cliente"
```

Respuesta básica de sandbox:
```json
{
  "success": true,
  "data": {
    "plate": "CLIENTE123",
    "make": "TOYOTA",
    "model": "COROLLA XEI CVT",
    "year": 2021,
    "firstRegistered": "2021-04-18",
    "totalOwners": 2,
    "currentOwners": 1,
    "currentLocation": {
      "city": "CABA",
      "province": "Ciudad Autónoma de Buenos Aires"
    },
    "locations": [
      "CABA, Ciudad Autónoma de Buenos Aires",
      "LA PLATA, Buenos Aires"
    ],
    "sourceDate": "2026-07-07",
    "classification": {
      "vehicleType": "AUTOMOTOR",
      "bodyType": "SEDAN",
      "vehicleCategory": "auto",
      "confidence": "high",
      "source": "sandbox",
      "matchedModel": "COROLLA XEI CVT 4 PUERTAS",
      "matchedYear": 2021,
      "approximateMatch": false,
      "description": "Respuesta mock de sandbox",
      "cacheHit": false
    },
    "sandbox": {
      "enabled": true,
      "scenario": "debt",
      "seed": "demo-cliente"
    }
  }
}
```

5. Crear un reporte completo:

```bash
curl -X POST "https://sandbox.clasific.ar/v1/reports?sandbox_scenario=debt&sandbox_seed=demo-cliente" \
  -H "x-api-key: clk_tu_api_key" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: demo-cliente-001" \
  -d '{
    "plate": "CLIENTE-123"
  }'
```

Respuesta inicial:
```json
{
  "id": "rep_11111111111141118111111111111111",
  "status": "processing",
  "plate": "CLIENTE123",
  "report": null,
  "createdAt": "2026-07-07T12:00:00.000Z",
  "updatedAt": "2026-07-07T12:00:00.000Z",
  "completedAt": null
}
```

6. Poll hasta completar:

```bash
curl "https://sandbox.clasific.ar/v1/reports/rep_11111111111141118111111111111111" \
  -H "x-api-key: clk_tu_api_key"
```

Respuesta completa:
```json
{
  "id": "rep_11111111111141118111111111111111",
  "status": "completed",
  "plate": "CLIENTE123",
  "report": {
    "vehicle": {
      "plate": "CLIENTE123",
      "make": "TOYOTA",
      "model": "COROLLA XEI CVT",
      "year": 2021,
      "vehicleType": "AUTOMOTOR",
      "firstRegistered": "2010-09-07",
      "totalOwners": 2,
      "currentOwners": 1,
      "registryOffice": "02044 - CAPITAL FEDERAL N 044",
      "currentLocation": {
        "city": "CABA",
        "province": "CAPITAL FEDERAL"
      },
      "locations": [
        "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
        "CABA, CAPITAL FEDERAL"
      ],
      "sourceDate": "2026-05-15",
      "queryDate": "2026-05-15",
      "quote": null
    },
    "possibleOwners": [
      {
        "name": "V******, S*",
        "id": {
          "type": "CUIT",
          "value": "30*"
        }
      }
    ],
    "results": {
      "fines": {
        "total": 1,
        "amountTotal": 427495.5,
        "currency": "ARS",
        "items": [
          {
            "date": "2023-09-24T08:48:00-03:00",
            "amount": 427495.5,
            "status": "voluntary_payment",
            "authority": null,
            "reference": "Q29775884",
            "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
            "jurisdiction": "CABA",
            "dueDate": null,
            "discountedAmount": null
          }
        ]
      },
      "vtv": {
        "total": 1,
        "items": [
          {
            "type": "vtv",
            "date": "2024-08-26",
            "sticker": "4581967",
            "inspectionType": "inspection",
            "detail": "Particulares",
            "result": "approved",
            "certificate": "6227143",
            "jurisdiction": "CABA",
            "facility": "9 de Julio Sur",
            "dueDate": "2025-06-01"
          }
        ]
      },
      "tax_debt": {
        "total": 1,
        "amountTotal": 235757.86,
        "currency": "ARS",
        "items": [
          {
            "period": "2026/3",
            "description": "POSICION",
            "dueDate": "2026-06-22",
            "amount": 235757.86,
            "updatedAmount": 235757.86,
            "status": "expired",
            "jurisdiction": "CABA"
          }
        ]
      },
      "cng": {
        "total": 1,
        "items": [
          {
            "operationDate": "2024-03-12",
            "workshop": "Taller GNC Ejemplo",
            "workshopTaxId": "30-00000000-0",
            "workshopCode": "GNC-123",
            "wafer": "12345678",
            "previousWafer": "87654321"
          }
        ]
      }
    },
    "digest": {
      "summary": "El historial registra infracciones y deuda de patente en CABA.",
      "headline": "Multas y deuda de patente detectadas",
      "riskLevel": "high",
      "highlights": [
        "1 infracción en CABA por al menos $427.496.",
        "Deuda de patente en CABA por aproximadamente $235.758."
      ],
      "issues": [
        {
          "title": "Infracciones en CABA",
          "severity": "high",
          "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
        }
      ],
      "positives": [],
      "noRecords": [],
      "ownershipEstimate": {
        "estimatedHistoricalOwnerCount": 2,
        "confidence": "medium",
        "basis": "El resumen registral consolidado muestra 2 titulares históricos."
      },
      "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
    }
  },
  "createdAt": "2026-07-07T12:00:00.000Z",
  "updatedAt": "2026-07-07T12:01:44.000Z",
  "completedAt": "2026-07-07T12:01:44.000Z"
}
```

7. Revisar entregas de webhook:

```bash
curl "https://sandbox.clasific.ar/v1/webhooks/deliveries?limit=10" \
  -H "x-api-key: clk_tu_api_key"
```

Entrega de sandbox:
```json
{
  "success": true,
  "data": {
    "deliveries": [
      {
        "id": "whd_sandbox_123",
        "report_request_id": "rep_11111111111141118111111111111111",
        "owner_ref": "user_cliente",
        "plate": "CLIENTE123",
        "report_status": "completed",
        "event_type": "report.completed",
        "is_test": false,
        "url": "https://webhook.site/tu-url-unica",
        "status": "processed",
        "attempts": 1,
        "max_attempts": 5,
        "response_status": 200,
        "last_error": null,
        "payload": {
          "eventId": "evt_22222222222242228222222222222222",
          "event": "report.completed",
          "id": "rep_11111111111141118111111111111111",
          "status": "completed",
          "plate": "CLIENTE123",
          "report": {
            "vehicle": {
              "plate": "CLIENTE123",
              "make": "TOYOTA",
              "model": "COROLLA XEI CVT",
              "year": 2021,
              "vehicleType": "AUTOMOTOR",
              "firstRegistered": "2010-09-07",
              "totalOwners": 2,
              "currentOwners": 1,
              "registryOffice": "02044 - CAPITAL FEDERAL N 044",
              "currentLocation": {
                "city": "CABA",
                "province": "CAPITAL FEDERAL"
              },
              "locations": [
                "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
                "CABA, CAPITAL FEDERAL"
              ],
              "sourceDate": "2026-05-15",
              "queryDate": "2026-05-15",
              "quote": null
            },
            "possibleOwners": [
              {
                "name": "V******, S*",
                "id": {
                  "type": "CUIT",
                  "value": "30*"
                }
              }
            ],
            "results": {
              "fines": {
                "total": 1,
                "amountTotal": 427495.5,
                "currency": "ARS",
                "items": [
                  {
                    "date": "2023-09-24T08:48:00-03:00",
                    "amount": 427495.5,
                    "status": "voluntary_payment",
                    "authority": null,
                    "reference": "Q29775884",
                    "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
                    "jurisdiction": "CABA",
                    "dueDate": null,
                    "discountedAmount": null
                  }
                ]
              },
              "vtv": {
                "total": 1,
                "items": [
                  {
                    "type": "vtv",
                    "date": "2024-08-26",
                    "sticker": "4581967",
                    "inspectionType": "inspection",
                    "detail": "Particulares",
                    "result": "approved",
                    "certificate": "6227143",
                    "jurisdiction": "CABA",
                    "facility": "9 de Julio Sur",
                    "dueDate": "2025-06-01"
                  }
                ]
              },
              "tax_debt": {
                "total": 1,
                "amountTotal": 235757.86,
                "currency": "ARS",
                "items": [
                  {
                    "period": "2026/3",
                    "description": "POSICION",
                    "dueDate": "2026-06-22",
                    "amount": 235757.86,
                    "updatedAmount": 235757.86,
                    "status": "expired",
                    "jurisdiction": "CABA"
                  }
                ]
              },
              "cng": {
                "total": 1,
                "items": [
                  {
                    "operationDate": "2024-03-12",
                    "workshop": "Taller GNC Ejemplo",
                    "workshopTaxId": "30-00000000-0",
                    "workshopCode": "GNC-123",
                    "wafer": "12345678",
                    "previousWafer": "87654321"
                  }
                ]
              }
            },
            "digest": {
              "summary": "El historial registra infracciones y deuda de patente en CABA.",
              "headline": "Multas y deuda de patente detectadas",
              "riskLevel": "high",
              "highlights": [
                "1 infracción en CABA por al menos $427.496.",
                "Deuda de patente en CABA por aproximadamente $235.758."
              ],
              "issues": [
                {
                  "title": "Infracciones en CABA",
                  "severity": "high",
                  "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
                }
              ],
              "positives": [],
              "noRecords": [],
              "ownershipEstimate": {
                "estimatedHistoricalOwnerCount": 2,
                "confidence": "medium",
                "basis": "El resumen registral consolidado muestra 2 titulares históricos."
              },
              "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
            }
          },
          "createdAt": "2026-07-07T12:00:00.000Z",
          "updatedAt": "2026-07-07T12:01:44.000Z",
          "completedAt": "2026-07-07T12:01:44.000Z"
        },
        "next_attempt_at": null,
        "processed_at": "2026-07-07T12:01:45.000Z",
        "created_at": "2026-07-07T12:01:44.000Z",
        "updated_at": "2026-07-07T12:01:45.000Z"
      }
    ],
    "total": 1
  }
}
```

## Contrato de reporte inteligente

Toda operación async de Reports devuelve el mismo DTO público, sin wrappers `success/data`:
- `id`: identificador opaco `rep_...`.
- `status`: `queued | processing | completed | failed`.
- `plate`.
- `report`: `null` mientras procesa o falla; objeto normalizado al completar.
- `createdAt`
- `updatedAt`
- `completedAt`

No se publican `reportRequestId`, `pollUrl`, `pollCount`, passes, progreso, nombres o conteos de sources ni errores de scrapers. Construí el polling como `GET /v1/reports/:id` con el `id` recibido.

Estados de reporte:
- `queued`: Solicitud aceptada; aún no comenzó la ejecución.
- `processing`: El reporte se está procesando. La ejecución interna no forma parte del contrato.
- `completed`: Reporte utilizable terminado; report contiene el contrato final.
- `failed`: No se pudo producir el reporte; report es null y aparece error.

El reporte final vive en `report`. Los webhooks agregan `eventId` y `event` al mismo DTO; `eventId` es estable durante reintentos y sirve para deduplicar entregas.

Campos principales del reporte:
- `vehicle`
- `possibleOwners`
- `results`: `fines`, `vtv`, `tax_debt`, `cng`.
- `digest`: síntesis de producto sin scrapers, fuentes fallidas ni cobertura interna.

`vehicle` consolida el antiguo resumen duplicado y los candidatos de titularidad se publican siempre en `possibleOwners[]`. Los items no repiten la patente ni exponen `source`. Reports reutiliza literalmente los mismos DTOs y normalizadores que Modules para `fines`, `vtv` y `tax_debt`; el cuarto dominio se llama siempre `cng`, nunca `gas` o `gnc`.

`vehicle.queryDate` es exclusivamente la fecha de consulta registral y no reutiliza `createdAt`; si el dato registral no informa esa fecha, vale `null`. `vehicle.quote` vale `null` cuando no hubo cotización o contiene exactamente `estimatedPrice`, `referencePrice`, `minPrice`, `maxPrice`, `currency` y `confidence`. Proveedores, inputs y metadatos del cálculo no forman parte del contrato público.

`digest.ownershipEstimate.estimatedHistoricalOwnerCount` es una estimación del historial de titulares. No representa la cantidad de elementos de `possibleOwners`, que contiene únicamente candidatos concretos identificados, ni reemplaza el conteo registral `vehicle.totalOwners`.

El digest nunca contradice el detalle: si los conteos o importes de un agregado histórico no reconcilian con los items públicos, se devuelve `digest: null`.

Si falla una fuente secundaria pero el producto sigue siendo utilizable, Reports termina `completed`. Por ahora Reports no emite `partial`.

Contrato de `results.fines.items[]`:
- `date`
- `amount`
- `status`
- `authority`
- `reference`
- `description`
- `jurisdiction`
- `dueDate`
- `discountedAmount`

Ejemplo de reporte normalizado:
```json
{
  "vehicle": {
    "plate": "JFK106",
    "make": "LAND ROVER",
    "model": "RANGE ROVER 3.6 HSE TDV8",
    "year": 2010,
    "vehicleType": "AUTOMOTOR",
    "firstRegistered": "2010-09-07",
    "totalOwners": 2,
    "currentOwners": 1,
    "registryOffice": "02044 - CAPITAL FEDERAL N 044",
    "currentLocation": {
      "city": "CABA",
      "province": "CAPITAL FEDERAL"
    },
    "locations": [
      "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
      "CABA, CAPITAL FEDERAL"
    ],
    "sourceDate": "2026-05-15",
    "queryDate": "2026-05-15",
    "quote": null
  },
  "possibleOwners": [
    {
      "name": "V******, S*",
      "id": {
        "type": "CUIT",
        "value": "30*"
      }
    }
  ],
  "results": {
    "fines": {
      "total": 1,
      "amountTotal": 427495.5,
      "currency": "ARS",
      "items": [
        {
          "date": "2023-09-24T08:48:00-03:00",
          "amount": 427495.5,
          "status": "voluntary_payment",
          "authority": null,
          "reference": "Q29775884",
          "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
          "jurisdiction": "CABA",
          "dueDate": null,
          "discountedAmount": null
        }
      ]
    },
    "vtv": {
      "total": 1,
      "items": [
        {
          "type": "vtv",
          "date": "2024-08-26",
          "sticker": "4581967",
          "inspectionType": "inspection",
          "detail": "Particulares",
          "result": "approved",
          "certificate": "6227143",
          "jurisdiction": "CABA",
          "facility": "9 de Julio Sur",
          "dueDate": "2025-06-01"
        }
      ]
    },
    "tax_debt": {
      "total": 1,
      "amountTotal": 235757.86,
      "currency": "ARS",
      "items": [
        {
          "period": "2026/3",
          "description": "POSICION",
          "dueDate": "2026-06-22",
          "amount": 235757.86,
          "updatedAmount": 235757.86,
          "status": "expired",
          "jurisdiction": "CABA"
        }
      ]
    },
    "cng": {
      "total": 1,
      "items": [
        {
          "operationDate": "2024-03-12",
          "workshop": "Taller GNC Ejemplo",
          "workshopTaxId": "30-00000000-0",
          "workshopCode": "GNC-123",
          "wafer": "12345678",
          "previousWafer": "87654321"
        }
      ]
    }
  },
  "digest": {
    "summary": "El historial registra infracciones y deuda de patente en CABA.",
    "headline": "Multas y deuda de patente detectadas",
    "riskLevel": "high",
    "highlights": [
      "1 infracción en CABA por al menos $427.496.",
      "Deuda de patente en CABA por aproximadamente $235.758."
    ],
    "issues": [
      {
        "title": "Infracciones en CABA",
        "severity": "high",
        "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
      }
    ],
    "positives": [],
    "noRecords": [],
    "ownershipEstimate": {
      "estimatedHistoricalOwnerCount": 2,
      "confidence": "medium",
      "basis": "El resumen registral consolidado muestra 2 titulares históricos."
    },
    "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
  }
}
```

## Estados y resultados de Modules

El job usa `queued → processing → completed|failed`. El job no usa `partial`: si la ejecución terminó y al menos un módulo produjo un resultado utilizable, el job queda `completed`.

Todo job async usa el mismo lifecycle público: `id`, `status`, `plate`, `createdAt`, `updatedAt` y `completedAt`. `startedAt` es telemetría interna y no forma parte del contrato.

Cada módulo usa `queued → processing → completed|partial|failed`:
- `completed`: terminó y el resultado es utilizable con la cobertura material esperada.
- `partial`: terminó y el resultado sigue siendo utilizable, pero una parte material de la cobertura no pudo verificarse. En este caso aparece `warnings[]` con `code=reduced_coverage`.
- `failed`: no se pudo producir un resultado utilizable para ese módulo.

Los nombres y conteos de fuentes internas nunca forman parte del response. No dependas de cantidad de scrapers, IDs internos ni métricas `checked/unavailable/total`. Una falla secundaria tampoco degrada automáticamente el módulo.

Cada módulo tiene su propio schema de datos: `fines` y `tax_debt` incluyen `total`, `amountTotal`, `currency: "ARS"` e `items`; `vtv` incluye sólo `total` e `items`. Como no hay paginación ni truncamiento, siempre se cumple `total === items.length`; los totales monetarios corresponden a esos mismos items. La patente vive a nivel job y no se repite en cada item. En VTV/RTO, `result` describe el resultado, `inspectionType` distingue `inspection`, `reinspection` o `unknown`, y `status` queda reservado para el job o módulo.

Los items tienen claves públicas estables en inglés:
- `fines.items[]`: `date`, `amount`, `status`, `authority`, `reference`, `description`, `jurisdiction`, `dueDate`, `discountedAmount`.
- `vtv.items[]`: `type`, `date`, `sticker`, `inspectionType`, `detail`, `result`, `certificate`, `jurisdiction`, `facility`, `dueDate`.
- `tax_debt.items[]`: `period`, `description`, `dueDate`, `amount`, `updatedAmount`, `status`, `jurisdiction`.

Los valores categóricos de `status`, `result`, `inspectionType` y `type` se normalizan como `lower_snake_case`. Los resultados conocidos de VTV/RTO son `approved`, `conditional`, `rejected` o `expired`, incluso cuando el proveedor usa códigos numéricos; cualquier otro valor se convierte en `unknown` y no se filtra al cliente. Cuando falta otro estado categórico también se devuelve `unknown`, nunca `null`. Las fechas válidas sin hora usan `YYYY-MM-DD`; cuando existe hora local se devuelve un timestamp ISO 8601 con offset. Un valor irreconocible o una fecha calendario inválida se devuelve como `null`, nunca con el formato interno del scraper.

## Dedupe e idempotencia de Modules

`POST /v1/vehicles/modules` admite como máximo un job activo por owner, entorno y patente. Se considera activo un job en estado `queued` o `processing`. Pueden coexistir jobs activos de patentes distintas. La regla se comparte entre todas las API keys del mismo owner y no bloquea reportes Intelligent.

Casos:
- No existe un job activo para esa patente: 202 · job nuevo. Consume 1 unidad por módulo.
- Misma Idempotency-Key + mismo payload: 200 · job original. No suma consumo, aunque ya haya terminado.
- Misma patente activa + mismos módulos: 200 · job activo. No suma consumo.
- Misma patente activa + otros módulos: 409 · module_query_in_progress. No suma consumo; devuelve el job a consultar.

Reglas de integración:
- La comparación usa la patente normalizada y el conjunto de módulos; el orden del array no crea un request diferente.
- Si coinciden patente y módulos con un job activo, la API responde `200` con el mismo ID, `meta.reused=true` y `meta.reason=query_in_progress`.
- Si la patente coincide pero cambia el conjunto de módulos, la API responde `409 module_query_in_progress` con el lifecycle de `active_query`. Construí el polling como `GET /v1/vehicles/modules/:id` usando ese `id`. No se crea ni se factura otro job.
- `Idempotency-Key` es un header opcional, no un atributo del body. Es recomendable usar una key nueva por operación lógica.
- Repetir la misma key con el mismo payload devuelve el job original con `200` y `meta.reason=idempotency_replay`, incluso si ya terminó. No suma consumo.
- Repetir la misma key con otro payload devuelve `409 idempotency_conflict` y no suma consumo.
- Una vez terminal el job, un request sin su key idempotente se considera nuevo y consume una unidad por cada módulo aceptado.

Respuesta al reutilizar un job activo:
```json
{
  "id": "vmq_0198abc123def456",
  "status": "processing",
  "plate": "ABC123",
  "modules": [
    "fines",
    "vtv"
  ],
  "createdAt": "2026-08-14T12:00:00.000Z",
  "updatedAt": "2026-08-14T12:00:01.000Z",
  "completedAt": null,
  "meta": {
    "reused": true,
    "reason": "query_in_progress"
  }
}
```

Respuesta si la patente está activa con otros módulos:
```json
{
  "error_code": "module_query_in_progress",
  "message": "Ya existe una consulta activa para esa patente con otros módulos.",
  "active_query": {
    "id": "vmq_0198abc123def456",
    "status": "processing",
    "plate": "ABC123",
    "modules": [
      "fines",
      "vtv"
    ],
    "createdAt": "2026-08-14T12:00:00.000Z",
    "updatedAt": "2026-08-14T12:00:01.000Z",
    "completedAt": null
  }
}
```

## Endpoints

### Consulta Básica de Vehículo

`GET /v1/vehicles/basic`

Retorna datos del vehículo desde la base histórica sin datos personales. La consulta estándar está disponible en Free; las búsquedas ante misses y los enriquecimientos CODIA y neumáticos requieren un plan pago o Custom.

Acceso a opciones pagas: onMiss=search, codia=true y tires=true requieren Starter, Growth, Scale o Custom. Con una API key Free, la API responde 403 PAID_PLAN_REQUIRED.


- Auth: requiere header `x-api-key`
- Cuota: una consulta Basic mensual compartida entre V1 y V2
- Planes: free, starter, growth, scale, custom

Parámetros:
- `plate` (string, requerido, query): Patente argentina (ABC123 o AB123CD)
- `classification` (string, opcional, query): Usá true para incluir clasificación inferida y cacheada del vehículo
- `onMiss` (string, opcional, query): Usá search para encolar una búsqueda nueva cuando la patente no está en la base histórica; responde 202 mientras se procesa
- `codia` (string, opcional, query): Usá true para incluir candidatos del catálogo CODIA activo
- `tires` (string, opcional, query): Usá true para incluir medidas de neumáticos compatibles de los catálogos activos

Headers específicos:
No aplica.

Ejemplos:
- Buscar cuando no existe en la base histórica: `https://api.clasific.ar/v1/vehicles/basic?plate=ABC123&onMiss=search` — Si no hay datos locales, devuelve 202 con status.state=searching.
- Agregar CODIA y medidas de neumáticos: `https://api.clasific.ar/v1/vehicles/basic?plate=ABC123&codia=true&tires=true`


Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "success": true,
  "data": {
    "plate": "ABC123",
    "make": "TOYOTA",
    "model": "COROLLA 1.8 SE-G CVT",
    "year": 2019,
    "firstRegistered": "2019-03-15",
    "totalOwners": 3,
    "currentOwners": 1,
    "currentLocation": {
      "city": "CAPITAL FEDERAL",
      "province": "Ciudad Autónoma de Buenos Aires"
    },
    "locations": [
      "CAPITAL FEDERAL, Ciudad Autónoma de Buenos Aires",
      "LA PLATA, Buenos Aires"
    ],
    "sourceDate": "2024-08-01",
    "classification": {
      "vehicleType": "AUTOMOTOR",
      "bodyType": "SEDAN",
      "vehicleCategory": "auto",
      "confidence": "medium",
      "source": "afip_model_match",
      "matchedModel": "COROLLA SE-G 1.8 CVT 4 PUERTAS",
      "matchedYear": 2019,
      "approximateMatch": true,
      "description": null,
      "cacheHit": false
    }
  }
}
```

Response 200 not found example:
```json
{
  "found": false
}
```


Response 202 Accepted — búsqueda en proceso example:
```json
{
  "success": true,
  "data": null,
  "status": {
    "state": "searching",
    "plate": "ABC123"
  }
}
```

Response 403 Forbidden — requiere plan pago example:
```json
{
  "error_code": "PAID_PLAN_REQUIRED",
  "message": "onMiss=search, codia=true, and tires=true require a paid or Custom plan."
}
```

---

### Crear consulta modular

`POST /v1/vehicles/modules`

Ejecuta de forma asíncrona sólo los módulos habilitados en el contrato Custom. El tiempo de resolución depende de los módulos y organismos consultados: esperá el resultado mediante polling o webhook, sin asumir una duración fija. Idempotency-Key es opcional pero recomendado. Mientras una patente tenga un job activo, no puede crearse otro: un request con los mismos módulos reutiliza el job y uno con módulos diferentes devuelve 409. Sólo un job realmente nuevo consume unidades.


- Auth: requiere header `x-api-key`
- Cuota: una unidad por módulo
- Planes: custom

Parámetros:
No aplica.

Headers específicos:
- `Idempotency-Key` (opcional): Recomendado para retries. La misma key + payload devuelve el job original sin doble consumo, incluso después de finalizar



Body JSON:
- `plate` (string, requerido, body): Patente argentina (ABC123 o AB123CD)
- `modules` (string[], requerido, body): Lista sin duplicados: fines, vtv y/o tax_debt

Request example:
```json
{
  "plate": "ABC123",
  "modules": [
    "fines",
    "vtv",
    "tax_debt"
  ]
}
```

Response 202 example:
```json
{
  "id": "vmq_0198abc123def456",
  "status": "queued",
  "plate": "ABC123",
  "modules": [
    "fines",
    "vtv",
    "tax_debt"
  ],
  "createdAt": "2026-08-14T12:00:00.000Z",
  "updatedAt": "2026-08-14T12:00:00.000Z",
  "completedAt": null
}
```


Response 200 OK — job activo reutilizado example:
```json
{
  "id": "vmq_0198abc123def456",
  "status": "processing",
  "plate": "ABC123",
  "modules": [
    "fines",
    "vtv"
  ],
  "createdAt": "2026-08-14T12:00:00.000Z",
  "updatedAt": "2026-08-14T12:00:01.000Z",
  "completedAt": null,
  "meta": {
    "reused": true,
    "reason": "query_in_progress"
  }
}
```

Response 409 Conflict — patente con otros módulos en curso example:
```json
{
  "error_code": "module_query_in_progress",
  "message": "Ya existe una consulta activa para esa patente con otros módulos.",
  "active_query": {
    "id": "vmq_0198abc123def456",
    "status": "processing",
    "plate": "ABC123",
    "modules": [
      "fines",
      "vtv"
    ],
    "createdAt": "2026-08-14T12:00:00.000Z",
    "updatedAt": "2026-08-14T12:00:01.000Z",
    "completedAt": null
  }
}
```

Response 409 Conflict — Idempotency-Key usada con otro payload example:
```json
{
  "error_code": "idempotency_conflict",
  "message": "Ese Idempotency-Key ya fue utilizado con otra consulta."
}
```

Response 403 Forbidden — módulo no contratado example:
```json
{
  "error_code": "module_not_enabled",
  "message": "Uno de los módulos solicitados no está habilitado en el contrato."
}
```

---

### Consultar estado modular

`GET /v1/vehicles/modules/:id`

Consulta el job sin consumo comercial adicional. El job usa queued, processing, completed o failed. Cada módulo usa queued, processing, completed, partial o failed; partial significa que terminó con datos utilizables pero con una reducción material de cobertura.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: custom

Parámetros:
- `id` (string, requerido, path): ID vmq_ devuelto al crear la consulta

Headers específicos:
No aplica.



Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "id": "vmq_0198abc123def456",
  "status": "completed",
  "plate": "ABC123",
  "modules": {
    "fines": {
      "status": "completed",
      "data": {
        "total": 1,
        "amountTotal": 47499.5,
        "currency": "ARS",
        "items": [
          {
            "date": "2024-05-03T15:53:00-03:00",
            "amount": 47499.5,
            "status": "voluntary_payment",
            "authority": null,
            "reference": "I06223680",
            "description": "Conducir sin portar la licencia",
            "jurisdiction": "CABA",
            "dueDate": null,
            "discountedAmount": null
          }
        ]
      }
    },
    "vtv": {
      "status": "completed",
      "data": {
        "total": 1,
        "items": [
          {
            "type": "rto",
            "date": "2012-10-01",
            "sticker": null,
            "inspectionType": "unknown",
            "detail": "Uso Particular",
            "result": "approved",
            "certificate": "B-207381",
            "jurisdiction": "Nacional",
            "facility": "108",
            "dueDate": "2013-09-25"
          }
        ]
      }
    },
    "tax_debt": {
      "status": "completed",
      "data": {
        "total": 0,
        "amountTotal": 0,
        "currency": "ARS",
        "items": []
      }
    }
  },
  "createdAt": "2026-08-14T12:00:00.000Z",
  "updatedAt": "2026-08-14T12:00:18.000Z",
  "completedAt": "2026-08-14T12:00:18.000Z"
}
```


Response 200 OK — módulo con cobertura material reducida example:
```json
{
  "id": "vmq_0198abc123def456",
  "status": "completed",
  "plate": "ABC123",
  "modules": {
    "fines": {
      "status": "partial",
      "warnings": [
        {
          "code": "reduced_coverage",
          "message": "Some material coverage could not be verified during this request."
        }
      ],
      "data": {
        "total": 0,
        "amountTotal": 0,
        "currency": "ARS",
        "items": []
      }
    }
  },
  "createdAt": "2026-08-14T12:00:00.000Z",
  "updatedAt": "2026-08-14T12:00:18.000Z",
  "completedAt": "2026-08-14T12:00:18.000Z"
}
```

---

### Crear Reporte Inteligente

`POST /v1/reports`

Crea un reporte inteligente asincrónico y devuelve directamente el DTO público del job. El ID opaco usa prefijo rep_; si existe cache lista puede devolver el reporte final en la misma respuesta. Soporta Idempotency-Key para retries sin doble consumo. El resultado se envía al webhook activo configurado para el owner y el entorno.


- Auth: requiere header `x-api-key`
- Cuota: intelligent
- Planes: starter, growth, scale, custom

Parámetros:
No aplica.

Headers específicos:
- `Idempotency-Key` (opcional): Opcional y recomendado para retries sin crear otro reporte ni consumir cuota dos veces



Body JSON:
- `plate` (string, requerido, body): Patente argentina (ABC123 o AB123CD)

Request example:
```json
{
  "plate": "ABC123"
}
```

Response 202 example:
```json
{
  "id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
  "status": "processing",
  "plate": "ABC123",
  "report": null,
  "createdAt": "2026-05-15T12:00:00.000Z",
  "updatedAt": "2026-05-15T12:00:03.000Z",
  "completedAt": null
}
```


Response 200 OK — reporte completado disponible example:
```json
{
  "id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
  "status": "completed",
  "plate": "JFK106",
  "report": {
    "vehicle": {
      "plate": "JFK106",
      "make": "LAND ROVER",
      "model": "RANGE ROVER 3.6 HSE TDV8",
      "year": 2010,
      "vehicleType": "AUTOMOTOR",
      "firstRegistered": "2010-09-07",
      "totalOwners": 2,
      "currentOwners": 1,
      "registryOffice": "02044 - CAPITAL FEDERAL N 044",
      "currentLocation": {
        "city": "CABA",
        "province": "CAPITAL FEDERAL"
      },
      "locations": [
        "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
        "CABA, CAPITAL FEDERAL"
      ],
      "sourceDate": "2026-05-15",
      "queryDate": "2026-05-15",
      "quote": null
    },
    "possibleOwners": [
      {
        "name": "V******, S*",
        "id": {
          "type": "CUIT",
          "value": "30*"
        }
      }
    ],
    "results": {
      "fines": {
        "total": 1,
        "amountTotal": 427495.5,
        "currency": "ARS",
        "items": [
          {
            "date": "2023-09-24T08:48:00-03:00",
            "amount": 427495.5,
            "status": "voluntary_payment",
            "authority": null,
            "reference": "Q29775884",
            "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
            "jurisdiction": "CABA",
            "dueDate": null,
            "discountedAmount": null
          }
        ]
      },
      "vtv": {
        "total": 1,
        "items": [
          {
            "type": "vtv",
            "date": "2024-08-26",
            "sticker": "4581967",
            "inspectionType": "inspection",
            "detail": "Particulares",
            "result": "approved",
            "certificate": "6227143",
            "jurisdiction": "CABA",
            "facility": "9 de Julio Sur",
            "dueDate": "2025-06-01"
          }
        ]
      },
      "tax_debt": {
        "total": 1,
        "amountTotal": 235757.86,
        "currency": "ARS",
        "items": [
          {
            "period": "2026/3",
            "description": "POSICION",
            "dueDate": "2026-06-22",
            "amount": 235757.86,
            "updatedAmount": 235757.86,
            "status": "expired",
            "jurisdiction": "CABA"
          }
        ]
      },
      "cng": {
        "total": 1,
        "items": [
          {
            "operationDate": "2024-03-12",
            "workshop": "Taller GNC Ejemplo",
            "workshopTaxId": "30-00000000-0",
            "workshopCode": "GNC-123",
            "wafer": "12345678",
            "previousWafer": "87654321"
          }
        ]
      }
    },
    "digest": {
      "summary": "El historial registra infracciones y deuda de patente en CABA.",
      "headline": "Multas y deuda de patente detectadas",
      "riskLevel": "high",
      "highlights": [
        "1 infracción en CABA por al menos $427.496.",
        "Deuda de patente en CABA por aproximadamente $235.758."
      ],
      "issues": [
        {
          "title": "Infracciones en CABA",
          "severity": "high",
          "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
        }
      ],
      "positives": [],
      "noRecords": [],
      "ownershipEstimate": {
        "estimatedHistoricalOwnerCount": 2,
        "confidence": "medium",
        "basis": "El resumen registral consolidado muestra 2 titulares históricos."
      },
      "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
    }
  },
  "createdAt": "2026-05-15T12:00:00.000Z",
  "updatedAt": "2026-05-15T12:03:00.000Z",
  "completedAt": "2026-05-15T12:03:00.000Z"
}
```

---

### Consultar Estado de Reporte

`GET /v1/reports/:id`

Consulta el DTO público del reporte. El polling no consume cuota del plan ni expone contadores, passes, progreso o fuentes. Al completar, report contiene el informe normalizado completo.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: starter, growth, scale, custom

Parámetros:
- `id` (string, requerido, path): ID rep_ devuelto por POST /v1/reports

Headers específicos:
No aplica.

Ejemplos:
- Request: `https://api.clasific.ar/v1/reports/rep_8d2c7ef49d8b4c8fa27d1d8906229341`


Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
  "status": "completed",
  "plate": "JFK106",
  "report": {
    "vehicle": {
      "plate": "JFK106",
      "make": "LAND ROVER",
      "model": "RANGE ROVER 3.6 HSE TDV8",
      "year": 2010,
      "vehicleType": "AUTOMOTOR",
      "firstRegistered": "2010-09-07",
      "totalOwners": 2,
      "currentOwners": 1,
      "registryOffice": "02044 - CAPITAL FEDERAL N 044",
      "currentLocation": {
        "city": "CABA",
        "province": "CAPITAL FEDERAL"
      },
      "locations": [
        "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
        "CABA, CAPITAL FEDERAL"
      ],
      "sourceDate": "2026-05-15",
      "queryDate": "2026-05-15",
      "quote": null
    },
    "possibleOwners": [
      {
        "name": "V******, S*",
        "id": {
          "type": "CUIT",
          "value": "30*"
        }
      }
    ],
    "results": {
      "fines": {
        "total": 1,
        "amountTotal": 427495.5,
        "currency": "ARS",
        "items": [
          {
            "date": "2023-09-24T08:48:00-03:00",
            "amount": 427495.5,
            "status": "voluntary_payment",
            "authority": null,
            "reference": "Q29775884",
            "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
            "jurisdiction": "CABA",
            "dueDate": null,
            "discountedAmount": null
          }
        ]
      },
      "vtv": {
        "total": 1,
        "items": [
          {
            "type": "vtv",
            "date": "2024-08-26",
            "sticker": "4581967",
            "inspectionType": "inspection",
            "detail": "Particulares",
            "result": "approved",
            "certificate": "6227143",
            "jurisdiction": "CABA",
            "facility": "9 de Julio Sur",
            "dueDate": "2025-06-01"
          }
        ]
      },
      "tax_debt": {
        "total": 1,
        "amountTotal": 235757.86,
        "currency": "ARS",
        "items": [
          {
            "period": "2026/3",
            "description": "POSICION",
            "dueDate": "2026-06-22",
            "amount": 235757.86,
            "updatedAmount": 235757.86,
            "status": "expired",
            "jurisdiction": "CABA"
          }
        ]
      },
      "cng": {
        "total": 1,
        "items": [
          {
            "operationDate": "2024-03-12",
            "workshop": "Taller GNC Ejemplo",
            "workshopTaxId": "30-00000000-0",
            "workshopCode": "GNC-123",
            "wafer": "12345678",
            "previousWafer": "87654321"
          }
        ]
      }
    },
    "digest": {
      "summary": "El historial registra infracciones y deuda de patente en CABA.",
      "headline": "Multas y deuda de patente detectadas",
      "riskLevel": "high",
      "highlights": [
        "1 infracción en CABA por al menos $427.496.",
        "Deuda de patente en CABA por aproximadamente $235.758."
      ],
      "issues": [
        {
          "title": "Infracciones en CABA",
          "severity": "high",
          "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
        }
      ],
      "positives": [],
      "noRecords": [],
      "ownershipEstimate": {
        "estimatedHistoricalOwnerCount": 2,
        "confidence": "medium",
        "basis": "El resumen registral consolidado muestra 2 titulares históricos."
      },
      "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
    }
  },
  "createdAt": "2026-05-15T12:00:00.000Z",
  "updatedAt": "2026-05-15T12:03:00.000Z",
  "completedAt": "2026-05-15T12:03:00.000Z"
}
```


---

### Estado de Cuenta

`GET /v1/status`

Retorna el uso de cuotas a nivel owner y todas las API keys asociadas. En Custom incluye el estimado ARS de Basic + Reportes y, por separado, consumo de módulos en la moneda del período. Basic y módulos comparten consumo entre V1/V2; sharedUsage muestra el detalle por versión.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: free, starter, growth, scale, custom

Parámetros:
No aplica.

Headers específicos:
No aplica.



Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "success": true,
  "data": {
    "owner": "user_martin",
    "plan": "custom",
    "usage": {
      "fast": {
        "used": 42,
        "limit": 100
      },
      "intelligent": {
        "used": 2,
        "limit": 5
      },
      "miss": {
        "used": 8,
        "limit": 500
      }
    },
    "rateLimits": {
      "rpm_total": 60,
      "rpm_intelligent": 5
    },
    "resetAt": "2026-02-23T00:00:00.000Z",
    "customBilling": {
      "contract": {
        "status": "active"
      },
      "period": {
        "key": "2026-08",
        "start": "2026-08-01",
        "end": "2026-09-01"
      },
      "usage": {
        "basic": {
          "used": 43,
          "limit": 10000,
          "remaining": 9957
        },
        "report": {
          "used": 0,
          "limit": 10,
          "remaining": 10
        },
        "modules": {
          "used": 2004,
          "limit": 2000,
          "remaining": 0,
          "overage": 4,
          "subtotal_cents": 300,
          "currency": "USD"
        }
      },
      "billing": {
        "base": {
          "estimated_total_cents": 249400,
          "currency": "ARS",
          "pricing_mode": "automatic",
          "tier_name": "Basic"
        },
        "modules": {
          "estimated_overage_cents": 300,
          "currency": "USD"
        }
      }
    },
    "keys": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "prefix": "clk_AbCd",
        "last4": "xYzW",
        "name": "Production Key",
        "status": "active",
        "lastUsedAt": "2026-02-22T21:15:00.000Z",
        "createdAt": "2026-02-01T10:00:00.000Z"
      }
    ]
  }
}
```


---

### Ver configuración de Webhook

`GET /v1/webhooks/config`

Obtiene la URL, estado y secret del webhook del owner. No consume cuota de reporte.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: starter, growth, scale, custom

Parámetros:
No aplica.

Headers específicos:
No aplica.



Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "success": true,
  "data": {
    "owner_ref": "user_martin",
    "url": "https://tu-app.com/webhooks/clasificar",
    "secret": "whsec_1234567890",
    "enabled": true,
    "updated_at": "2026-05-15T12:00:00.000Z"
  }
}
```


---

### Actualizar Webhook

`PATCH /v1/webhooks/config`

Configura el webhook compartido por V1, V2 y Cuenta para el ambiente actual. rotate_secret=true rota el secreto de verificación de ambas versiones.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: starter, growth, scale, custom

Parámetros:
No aplica.

Headers específicos:
No aplica.



Body JSON:
- `url` (string | null, opcional, body): URL HTTPS que recibirá eventos. null desconfigura la URL.
- `enabled` (boolean, opcional, body): Activa o desactiva entregas.
- `rotate_secret` (boolean, opcional, body): Genera un secret nuevo para validar próximos eventos.

Request example:
```json
{
  "url": "https://tu-app.com/webhooks/clasificar",
  "enabled": true,
  "rotate_secret": false
}
```

Response 200 example:
```json
{
  "success": true,
  "data": {
    "owner_ref": "user_martin",
    "url": "https://tu-app.com/webhooks/clasificar",
    "secret": "whsec_1234567890",
    "enabled": true,
    "updated_at": "2026-05-15T12:00:00.000Z"
  }
}
```


---

### Listar entregas de Webhook

`GET /v1/webhooks/deliveries`

Lista entregas recientes. Permite filtrar por status=pending|processed|failed, limit y offset.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: starter, growth, scale, custom

Parámetros:
- `status` (string, opcional, query): pending, processed o failed
- `limit` (number, opcional, query): 1 a 100
- `offset` (number, opcional, query): Offset de paginación

Headers específicos:
No aplica.



Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "success": true,
  "data": {
    "deliveries": [
      {
        "id": "whd_123",
        "report_request_id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
        "module_query_id": null,
        "owner_ref": "user_martin",
        "environment": "production",
        "plate": "JFK106",
        "report_status": "completed",
        "event_type": "report.completed",
        "is_test": false,
        "url": "https://tu-app.com/webhooks/clasificar",
        "status": "processed",
        "attempts": 1,
        "max_attempts": 5,
        "response_status": 200,
        "last_error": null,
        "payload": {
          "eventId": "evt_7c684544c40e4e3a8d526eafc2f9514d",
          "event": "report.completed",
          "id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
          "status": "completed",
          "plate": "JFK106",
          "report": {
            "vehicle": {
              "plate": "JFK106",
              "make": "LAND ROVER",
              "model": "RANGE ROVER 3.6 HSE TDV8",
              "year": 2010,
              "vehicleType": "AUTOMOTOR",
              "firstRegistered": "2010-09-07",
              "totalOwners": 2,
              "currentOwners": 1,
              "registryOffice": "02044 - CAPITAL FEDERAL N 044",
              "currentLocation": {
                "city": "CABA",
                "province": "CAPITAL FEDERAL"
              },
              "locations": [
                "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
                "CABA, CAPITAL FEDERAL"
              ],
              "sourceDate": "2026-05-15",
              "queryDate": "2026-05-15",
              "quote": null
            },
            "possibleOwners": [
              {
                "name": "V******, S*",
                "id": {
                  "type": "CUIT",
                  "value": "30*"
                }
              }
            ],
            "results": {
              "fines": {
                "total": 1,
                "amountTotal": 427495.5,
                "currency": "ARS",
                "items": [
                  {
                    "date": "2023-09-24T08:48:00-03:00",
                    "amount": 427495.5,
                    "status": "voluntary_payment",
                    "authority": null,
                    "reference": "Q29775884",
                    "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
                    "jurisdiction": "CABA",
                    "dueDate": null,
                    "discountedAmount": null
                  }
                ]
              },
              "vtv": {
                "total": 1,
                "items": [
                  {
                    "type": "vtv",
                    "date": "2024-08-26",
                    "sticker": "4581967",
                    "inspectionType": "inspection",
                    "detail": "Particulares",
                    "result": "approved",
                    "certificate": "6227143",
                    "jurisdiction": "CABA",
                    "facility": "9 de Julio Sur",
                    "dueDate": "2025-06-01"
                  }
                ]
              },
              "tax_debt": {
                "total": 1,
                "amountTotal": 235757.86,
                "currency": "ARS",
                "items": [
                  {
                    "period": "2026/3",
                    "description": "POSICION",
                    "dueDate": "2026-06-22",
                    "amount": 235757.86,
                    "updatedAmount": 235757.86,
                    "status": "expired",
                    "jurisdiction": "CABA"
                  }
                ]
              },
              "cng": {
                "total": 1,
                "items": [
                  {
                    "operationDate": "2024-03-12",
                    "workshop": "Taller GNC Ejemplo",
                    "workshopTaxId": "30-00000000-0",
                    "workshopCode": "GNC-123",
                    "wafer": "12345678",
                    "previousWafer": "87654321"
                  }
                ]
              }
            },
            "digest": {
              "summary": "El historial registra infracciones y deuda de patente en CABA.",
              "headline": "Multas y deuda de patente detectadas",
              "riskLevel": "high",
              "highlights": [
                "1 infracción en CABA por al menos $427.496.",
                "Deuda de patente en CABA por aproximadamente $235.758."
              ],
              "issues": [
                {
                  "title": "Infracciones en CABA",
                  "severity": "high",
                  "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
                }
              ],
              "positives": [],
              "noRecords": [],
              "ownershipEstimate": {
                "estimatedHistoricalOwnerCount": 2,
                "confidence": "medium",
                "basis": "El resumen registral consolidado muestra 2 titulares históricos."
              },
              "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
            }
          },
          "createdAt": "2026-05-15T12:00:00.000Z",
          "updatedAt": "2026-05-15T12:03:00.000Z",
          "completedAt": "2026-05-15T12:03:00.000Z"
        },
        "next_attempt_at": null,
        "processed_at": "2026-05-15T12:03:04.000Z",
        "created_at": "2026-05-15T12:03:00.000Z",
        "updated_at": "2026-05-15T12:03:04.000Z"
      }
    ],
    "total": 1
  }
}
```


---

### Enviar Webhook de Prueba

`POST /v1/webhooks/test`

Envía un report.completed de prueba usando la URL configurada. Puede probarse aunque las entregas automáticas estén desactivadas.


- Auth: requiere header `x-api-key`
- Cuota: analytics-only
- Planes: starter, growth, scale, custom

Parámetros:
No aplica.

Headers específicos:
No aplica.



Body JSON:
No aplica.

Request example:
No aplica.

Response 200 example:
```json
{
  "success": true,
  "data": {
    "id": "whd_test_123",
    "event_type": "report.completed",
    "is_test": true,
    "status": "pending",
    "payload": {
      "eventId": "evt_7c684544c40e4e3a8d526eafc2f9514d",
      "event": "report.completed",
      "id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
      "status": "completed",
      "plate": "JFK106",
      "report": {
        "vehicle": {
          "plate": "JFK106",
          "make": "LAND ROVER",
          "model": "RANGE ROVER 3.6 HSE TDV8",
          "year": 2010,
          "vehicleType": "AUTOMOTOR",
          "firstRegistered": "2010-09-07",
          "totalOwners": 2,
          "currentOwners": 1,
          "registryOffice": "02044 - CAPITAL FEDERAL N 044",
          "currentLocation": {
            "city": "CABA",
            "province": "CAPITAL FEDERAL"
          },
          "locations": [
            "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
            "CABA, CAPITAL FEDERAL"
          ],
          "sourceDate": "2026-05-15",
          "queryDate": "2026-05-15",
          "quote": null
        },
        "possibleOwners": [
          {
            "name": "V******, S*",
            "id": {
              "type": "CUIT",
              "value": "30*"
            }
          }
        ],
        "results": {
          "fines": {
            "total": 1,
            "amountTotal": 427495.5,
            "currency": "ARS",
            "items": [
              {
                "date": "2023-09-24T08:48:00-03:00",
                "amount": 427495.5,
                "status": "voluntary_payment",
                "authority": null,
                "reference": "Q29775884",
                "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
                "jurisdiction": "CABA",
                "dueDate": null,
                "discountedAmount": null
              }
            ]
          },
          "vtv": {
            "total": 1,
            "items": [
              {
                "type": "vtv",
                "date": "2024-08-26",
                "sticker": "4581967",
                "inspectionType": "inspection",
                "detail": "Particulares",
                "result": "approved",
                "certificate": "6227143",
                "jurisdiction": "CABA",
                "facility": "9 de Julio Sur",
                "dueDate": "2025-06-01"
              }
            ]
          },
          "tax_debt": {
            "total": 1,
            "amountTotal": 235757.86,
            "currency": "ARS",
            "items": [
              {
                "period": "2026/3",
                "description": "POSICION",
                "dueDate": "2026-06-22",
                "amount": 235757.86,
                "updatedAmount": 235757.86,
                "status": "expired",
                "jurisdiction": "CABA"
              }
            ]
          },
          "cng": {
            "total": 1,
            "items": [
              {
                "operationDate": "2024-03-12",
                "workshop": "Taller GNC Ejemplo",
                "workshopTaxId": "30-00000000-0",
                "workshopCode": "GNC-123",
                "wafer": "12345678",
                "previousWafer": "87654321"
              }
            ]
          }
        },
        "digest": {
          "summary": "El historial registra infracciones y deuda de patente en CABA.",
          "headline": "Multas y deuda de patente detectadas",
          "riskLevel": "high",
          "highlights": [
            "1 infracción en CABA por al menos $427.496.",
            "Deuda de patente en CABA por aproximadamente $235.758."
          ],
          "issues": [
            {
              "title": "Infracciones en CABA",
              "severity": "high",
              "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
            }
          ],
          "positives": [],
          "noRecords": [],
          "ownershipEstimate": {
            "estimatedHistoricalOwnerCount": 2,
            "confidence": "medium",
            "basis": "El resumen registral consolidado muestra 2 titulares históricos."
          },
          "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
        }
      },
      "createdAt": "2026-05-15T12:00:00.000Z",
      "updatedAt": "2026-05-15T12:03:00.000Z",
      "completedAt": "2026-05-15T12:03:00.000Z"
    }
  }
}
```



## Webhooks

Los webhooks se envían como `POST` al endpoint configurado. Tu endpoint debe devolver cualquier `2xx` para marcar la entrega como procesada.

Config:
- URL única por owner y entorno: `PATCH /v1/webhooks/config`.
- Intelligent y Modules usan esta misma configuración; los requests no aceptan una URL alternativa.
- Secret: devuelto por `GET /v1/webhooks/config`.

Headers enviados:
- `x-clasificar-event`: `report.completed`, `report.failed` o `vehicle.modules.completed`.
- `x-clasificar-delivery`: ID único de entrega para idempotencia/logs.
- `x-clasificar-timestamp`: epoch Unix usado para limitar replay.
- `x-clasificar-signature`: HMAC-SHA256 `v1=<hex>` de `timestamp.rawBody` con el secret.

Validación recomendada:
- Rechazar timestamps fuera de una ventana breve (por ejemplo, cinco minutos).
- Recalcular el HMAC sobre el body crudo y compararlo en tiempo constante con `x-clasificar-signature`.
- Procesar cada `x-clasificar-delivery` de forma idempotente.

Eventos:
- `report.completed`: El reporte inteligente terminó correctamente. Payload: Agrega eventId y event al mismo DTO público que devuelve el polling; report contiene el informe normalizado.
- `report.failed`: El reporte inteligente no pudo completarse. Payload: Agrega eventId y event al mismo DTO público fallido; report=null y error usa un código estable.
- `vehicle.modules.completed`: Una consulta modular terminó en completed o failed. Payload: Incluye el mismo lifecycle público (id, status, plate y timestamps) y el resultado completed/partial/failed de cada módulo.

Estados de entrega:
- `pending`: Entrega pendiente o esperando reintento.
- `processed`: Tu endpoint respondió 2xx y la entrega quedó confirmada.
- `failed`: Se agotaron los intentos o el endpoint respondió error.

Payload `report.completed`:
```json
{
  "eventId": "evt_7c684544c40e4e3a8d526eafc2f9514d",
  "event": "report.completed",
  "id": "rep_8d2c7ef49d8b4c8fa27d1d8906229341",
  "status": "completed",
  "plate": "JFK106",
  "report": {
    "vehicle": {
      "plate": "JFK106",
      "make": "LAND ROVER",
      "model": "RANGE ROVER 3.6 HSE TDV8",
      "year": 2010,
      "vehicleType": "AUTOMOTOR",
      "firstRegistered": "2010-09-07",
      "totalOwners": 2,
      "currentOwners": 1,
      "registryOffice": "02044 - CAPITAL FEDERAL N 044",
      "currentLocation": {
        "city": "CABA",
        "province": "CAPITAL FEDERAL"
      },
      "locations": [
        "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
        "CABA, CAPITAL FEDERAL"
      ],
      "sourceDate": "2026-05-15",
      "queryDate": "2026-05-15",
      "quote": null
    },
    "possibleOwners": [
      {
        "name": "V******, S*",
        "id": {
          "type": "CUIT",
          "value": "30*"
        }
      }
    ],
    "results": {
      "fines": {
        "total": 1,
        "amountTotal": 427495.5,
        "currency": "ARS",
        "items": [
          {
            "date": "2023-09-24T08:48:00-03:00",
            "amount": 427495.5,
            "status": "voluntary_payment",
            "authority": null,
            "reference": "Q29775884",
            "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
            "jurisdiction": "CABA",
            "dueDate": null,
            "discountedAmount": null
          }
        ]
      },
      "vtv": {
        "total": 1,
        "items": [
          {
            "type": "vtv",
            "date": "2024-08-26",
            "sticker": "4581967",
            "inspectionType": "inspection",
            "detail": "Particulares",
            "result": "approved",
            "certificate": "6227143",
            "jurisdiction": "CABA",
            "facility": "9 de Julio Sur",
            "dueDate": "2025-06-01"
          }
        ]
      },
      "tax_debt": {
        "total": 1,
        "amountTotal": 235757.86,
        "currency": "ARS",
        "items": [
          {
            "period": "2026/3",
            "description": "POSICION",
            "dueDate": "2026-06-22",
            "amount": 235757.86,
            "updatedAmount": 235757.86,
            "status": "expired",
            "jurisdiction": "CABA"
          }
        ]
      },
      "cng": {
        "total": 1,
        "items": [
          {
            "operationDate": "2024-03-12",
            "workshop": "Taller GNC Ejemplo",
            "workshopTaxId": "30-00000000-0",
            "workshopCode": "GNC-123",
            "wafer": "12345678",
            "previousWafer": "87654321"
          }
        ]
      }
    },
    "digest": {
      "summary": "El historial registra infracciones y deuda de patente en CABA.",
      "headline": "Multas y deuda de patente detectadas",
      "riskLevel": "high",
      "highlights": [
        "1 infracción en CABA por al menos $427.496.",
        "Deuda de patente en CABA por aproximadamente $235.758."
      ],
      "issues": [
        {
          "title": "Infracciones en CABA",
          "severity": "high",
          "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
        }
      ],
      "positives": [],
      "noRecords": [],
      "ownershipEstimate": {
        "estimatedHistoricalOwnerCount": 2,
        "confidence": "medium",
        "basis": "El resumen registral consolidado muestra 2 titulares históricos."
      },
      "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
    }
  },
  "createdAt": "2026-05-15T12:00:00.000Z",
  "updatedAt": "2026-05-15T12:03:00.000Z",
  "completedAt": "2026-05-15T12:03:00.000Z"
}
```

Payload `report.failed`:
```json
{
  "eventId": "evt_9fd393058c5c4a87ab65ff507f0b4db1",
  "event": "report.failed",
  "id": "rep_fbeccf4d234d4dd289e476590c32e249",
  "status": "failed",
  "plate": "ABC123",
  "report": null,
  "createdAt": "2026-05-15T09:45:18.323Z",
  "updatedAt": "2026-05-15T09:46:39.093Z",
  "completedAt": "2026-05-15T09:46:39.093Z",
  "error": {
    "code": "report_generation_failed",
    "message": "The report could not be generated."
  }
}
```

Payload sandbox `report.completed`:
```json
{
  "eventId": "evt_22222222222242228222222222222222",
  "event": "report.completed",
  "id": "rep_11111111111141118111111111111111",
  "status": "completed",
  "plate": "CLIENTE123",
  "report": {
    "vehicle": {
      "plate": "CLIENTE123",
      "make": "TOYOTA",
      "model": "COROLLA XEI CVT",
      "year": 2021,
      "vehicleType": "AUTOMOTOR",
      "firstRegistered": "2010-09-07",
      "totalOwners": 2,
      "currentOwners": 1,
      "registryOffice": "02044 - CAPITAL FEDERAL N 044",
      "currentLocation": {
        "city": "CABA",
        "province": "CAPITAL FEDERAL"
      },
      "locations": [
        "C.AUTONOMA DE BS.AS, Ciudad Autonoma de Buenos Aires",
        "CABA, CAPITAL FEDERAL"
      ],
      "sourceDate": "2026-05-15",
      "queryDate": "2026-05-15",
      "quote": null
    },
    "possibleOwners": [
      {
        "name": "V******, S*",
        "id": {
          "type": "CUIT",
          "value": "30*"
        }
      }
    ],
    "results": {
      "fines": {
        "total": 1,
        "amountTotal": 427495.5,
        "currency": "ARS",
        "items": [
          {
            "date": "2023-09-24T08:48:00-03:00",
            "amount": 427495.5,
            "status": "voluntary_payment",
            "authority": null,
            "reference": "Q29775884",
            "description": "Exceso de velocidad de 10% a 30% más de la velocidad permitida",
            "jurisdiction": "CABA",
            "dueDate": null,
            "discountedAmount": null
          }
        ]
      },
      "vtv": {
        "total": 1,
        "items": [
          {
            "type": "vtv",
            "date": "2024-08-26",
            "sticker": "4581967",
            "inspectionType": "inspection",
            "detail": "Particulares",
            "result": "approved",
            "certificate": "6227143",
            "jurisdiction": "CABA",
            "facility": "9 de Julio Sur",
            "dueDate": "2025-06-01"
          }
        ]
      },
      "tax_debt": {
        "total": 1,
        "amountTotal": 235757.86,
        "currency": "ARS",
        "items": [
          {
            "period": "2026/3",
            "description": "POSICION",
            "dueDate": "2026-06-22",
            "amount": 235757.86,
            "updatedAmount": 235757.86,
            "status": "expired",
            "jurisdiction": "CABA"
          }
        ]
      },
      "cng": {
        "total": 1,
        "items": [
          {
            "operationDate": "2024-03-12",
            "workshop": "Taller GNC Ejemplo",
            "workshopTaxId": "30-00000000-0",
            "workshopCode": "GNC-123",
            "wafer": "12345678",
            "previousWafer": "87654321"
          }
        ]
      }
    },
    "digest": {
      "summary": "El historial registra infracciones y deuda de patente en CABA.",
      "headline": "Multas y deuda de patente detectadas",
      "riskLevel": "high",
      "highlights": [
        "1 infracción en CABA por al menos $427.496.",
        "Deuda de patente en CABA por aproximadamente $235.758."
      ],
      "issues": [
        {
          "title": "Infracciones en CABA",
          "severity": "high",
          "detail": "Registra 1 infracción por al menos $427.496 en pago voluntario."
        }
      ],
      "positives": [],
      "noRecords": [],
      "ownershipEstimate": {
        "estimatedHistoricalOwnerCount": 2,
        "confidence": "medium",
        "basis": "El resumen registral consolidado muestra 2 titulares históricos."
      },
      "recommendation": "Pedí el detalle actualizado de multas y deuda antes de avanzar."
    }
  },
  "createdAt": "2026-07-07T12:00:00.000Z",
  "updatedAt": "2026-07-07T12:01:44.000Z",
  "completedAt": "2026-07-07T12:01:44.000Z"
}
```

Payload sandbox `report.failed`:
```json
{
  "eventId": "evt_33333333333343338333333333333333",
  "event": "report.failed",
  "id": "rep_44444444444444448444444444444444",
  "status": "failed",
  "plate": "CLIENTE123",
  "report": null,
  "createdAt": "2026-07-07T12:00:00.000Z",
  "updatedAt": "2026-07-07T12:01:17.000Z",
  "completedAt": "2026-07-07T12:01:17.000Z",
  "error": {
    "code": "report_generation_failed",
    "message": "The report could not be generated."
  }
}
```

## Errores

Formato de errores de autenticación, permisos y cuota:
```json
{
  "error_code": "PAID_PLAN_REQUIRED",
  "message": "The requested option requires a paid or Custom plan."
}
```

Algunos errores de validación conservan el formato compatible:
```json
{
  "success": false,
  "error": "Mensaje de error legible"
}
```

Códigos:
- HTTP 401, `INVALID_API_KEY`: API key inválida, revocada o faltante
- HTTP 403, `PAID_PLAN_REQUIRED`: La opción solicitada requiere un plan pago o Custom
- HTTP 429, `RATE_LIMIT_EXCEEDED`: Límite RPM compartido por cuenta y entorno entre V1 y V2
- HTTP 429, `QUOTA_EXCEEDED`: Cupo Basic compartido agotado; details identifica el límite y su reset
- HTTP 429, `QUOTA_EXCEEDED_INTELLIGENT`: Cuota diaria intelligent agotada
- HTTP 429, `MONTHLY_QUOTA_EXCEEDED`: Cuota mensual agotada
- HTTP 429, `MISS_LIMIT_EXCEEDED`: Control legacy de misses para operaciones V1 que aún lo aplican; no rige Basic autenticado
- HTTP 403, `custom_plan_required`: El endpoint de módulos requiere un contrato Custom
- HTTP 403, `module_not_enabled`: Uno o más módulos no están habilitados en el contrato
- HTTP 409, `idempotency_conflict`: La Idempotency-Key ya fue utilizada con otro payload
- HTTP 409, `module_query_in_progress`: Ya existe una consulta activa para la patente con otro conjunto de módulos

Ejemplo de error de cuota:
```json
{
  "success": false,
  "error_code": "QUOTA_EXCEEDED",
  "message": "Account limit exceeded.",
  "reset_at": "2026-02-23T00:00:00.000Z"
}
```


---

Fuente: [Clasificar](https://clasific.ar) — Base de datos vehiculares argentinos.
Contenido informativo. No reemplaza una consulta oficial ni una validación legal.
