# 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:
- Todos los endpoints públicos están bajo `/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:
- 25 consultas por día.
- 100 consultas por mes.
- 5 requests por minuto.

## Precios API

Existe un plan gratuito para pruebas iniciales y planes pagos para mayor volumen. La tabla actualizada de precios,
límites mensuales, reportes inteligentes y RPM está disponible 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

Las cuotas se rastrean a nivel owner y por key:

- Cuotas diarias por owner: `fast`, `intelligent`, `miss`.
- Múltiples API keys comparten las cuotas diarias del owner.
- Rotar keys no resetea el uso.
- Rate limits por key: RPM total y RPM intelligent.

Consumo:
- Vehículo encontrado: consume `fast`.
- Vehículo no encontrado: consume `miss`.
- Reporte inteligente aceptado o servido desde cache: consume `intelligent` + cuota mensual.
- Polling de reporte inteligente: solo analytics, no consume cuota.

Importante:
- Cuando se alcanza el límite diario de `miss`, todas las búsquedas se bloquean, incluso fast.
- El reset diario ocurre a medianoche UTC.
- `/v1/status` devuelve `resetAt`.

## Sandbox

Sandbox usa el host `https://sandbox.clasific.ar` y el mismo header `x-api-key`. Cualquier API key válida puede llamar todos los endpoints documentados en sandbox, incluso si el owner está en plan gratuito.

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 `degraded`.

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|degraded|failed`:
- `completed`: terminó y el resultado es utilizable con la cobertura material esperada.
- `degraded`: 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: fast (+ miss)
- Planes: free, starter, growth, scale, custom

Parámetros query:
- `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 query:
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, query): Patente argentina (ABC123 o AB123CD)
- `modules` (string[], requerido, query): 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, degraded o failed; degraded 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 query:
- `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": "degraded",
      "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 query:
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, query): Patente argentina (ABC123 o AB123CD)

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

Response 200 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
}
```


---

### 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 query:
- `id` (string, requerido, path): ID rep_ devuelto por POST /v1/reports

Headers específicos:
No aplica.

Ejemplos:
- Request: `https://api.clasific.ar/v1/reports/:id?id=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 y overage de Modules en USD.


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

Parámetros query:
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 query:
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 único webhook del owner para reportes Intelligent y consultas Modules. rotate_secret=true rota el secreto de verificación.


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

Parámetros query:
No aplica.

Headers específicos:
No aplica.



Body JSON:
- `url` (string | null, opcional, query): URL HTTPS que recibirá eventos. null desconfigura la URL.
- `enabled` (boolean, opcional, query): Activa o desactiva entregas.
- `rotate_secret` (boolean, opcional, query): 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 query:
- `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 query:
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/degraded/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 total excedido (por key)
- HTTP 429, `INTELLIGENT_RATE_LIMIT_EXCEEDED`: Límite RPM intelligent excedido
- HTTP 429, `QUOTA_EXCEEDED_FAST`: Cuota diaria fast agotada (por owner)
- HTTP 429, `QUOTA_EXCEEDED_INTELLIGENT`: Cuota diaria intelligent agotada
- HTTP 429, `MONTHLY_QUOTA_EXCEEDED`: Cuota mensual agotada
- HTTP 429, `MISS_LIMIT_EXCEEDED`: Límite diario de misses alcanzado - bloquea todas las búsquedas
- 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_FAST",
  "message": "Fast lookup daily quota 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.
