Contenido

API de Chasis

La API de Chasis entrega informes de vehículos de Argentina a partir de la patente: multas de jurisdicciones de todo el país, deuda de patentes, VTV, GNC, recalls y el informe de dominio oficial de DNRPA (titulares, gravámenes, chasis y motor), con su PDF original. También responde al instante con los datos y la radicación de una patente según DNRPA (consulta básica).

Es una API REST sobre HTTPS, con JSON, autenticación por API key, webhooks firmados e idempotencia en las operaciones que consumen créditos.

URL base https://www.chasis.com.ar/api/v1
Autenticación Authorization: Bearer <API key>
Formato JSON UTF-8. Fechas ISO 8601 en UTC
Panel de la cuenta https://www.chasis.com.ar/cuenta (API keys, webhooks, avisos)
Cliente TypeScript https://www.chasis.com.ar/sdk/chasis.ts
Esta guía en Markdown https://www.chasis.com.ar/docs/api.md
Soporte get@chasis.com.ar (incluí el X-Request-Id de la respuesta)

Integración asistida por IA. Esta guía está escrita para que un desarrollador o un agente de IA pueda dejar una integración completa funcionando sin más contexto. Para un agente alcanza con la URL de la versión en Markdown y una API key; la sección Integración paso a paso es el plan a seguir.


Inicio rápido

1. Creá una API key en https://www.chasis.com.ar/cuenta/ajustes y guardala en el servidor como CHASIS_API_KEY.

2. Consultá tus créditos:

curl https://www.chasis.com.ar/api/v1/usage \
  -H "Authorization: Bearer $CHASIS_API_KEY"

3. Pedí un informe:

curl -X POST https://www.chasis.com.ar/api/v1/informes \
  -H "Authorization: Bearer $CHASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"patente":"AB123CD","tipo":"dominio"}'

4. Consultá el resultado con el id devuelto (o esperá el webhook):

curl https://www.chasis.com.ar/api/v1/informes/{id} \
  -H "Authorization: Bearer $CHASIS_API_KEY"

Conceptos

Tipos de informe y fases

Un informe se pide por patente argentina (formato ABC123 o AB123CD) y tiene una o dos fases:

tipo Fases Contenido Tiempo típico Créditos
dominio multas + dominio Informe de dominio oficial de DNRPA y su PDF original, más todo lo de chasis Multas: 15 minutos a 1 hora. Dominio: casi siempre menos de 24 h hábiles 1 dominio (las multas van incluidas)
chasis multas Datos del vehículo, multas de todo el país, deuda de patentes, VTV, GNC, recalls 15 minutos a 1 hora 1 chasis

completo (el valor por defecto) es el nombre anterior de dominio y funciona exactamente igual. Se sigue aceptando y el informe se devuelve con el tipo con que se pidió.

DNRPA emite los informes de dominio en días hábiles, de 8 a 17 h. Si no lo envía dentro de las 96 horas, lo verificamos con DNRPA: el pedido sigue en curso hasta que llegue el informe o hasta que confirmemos que no va a llegar. En ese caso la fase se marca como fallida, se devuelve el crédito y se avisa por webhook y email.

Consultas

Una consulta (POST /consultas) devuelve en el momento los datos de la patente según DNRPA: marca, modelo, año, tipo de vehículo, si tiene placa Mercosur y el registro seccional donde está radicado (nombre, dirección, localidad y provincia). No es un informe: no tiene fases, webhooks ni PDF, y no incluye titulares, gravámenes ni multas.

Cada consulta descuenta un crédito consulta. La respuesta queda guardada 15 días: si volvés a consultar la misma patente en ese plazo, se devuelve lo guardado y no se descuenta nada (reutilizada: true). Pasados los 15 días se consulta de nuevo, porque los datos pueden cambiar (por ejemplo, por una transferencia).

Estados

Cada fase tiene su propio estado: processing → completed o failed. El estado general del informe se deriva de sus fases:

status Significado
pending, processing Al menos una fase sigue en curso
completed Terminaron todas las fases y al menos una salió bien
failed Fallaron todas las fases

Un informe dominio puede estar en processing con la fase de multas ya terminada: sus datos están disponibles mientras se espera el dominio.

Créditos

Cada cuenta tiene créditos chasis, dominio y consulta, cada uno con su propio contador, en una de dos modalidades (billing):

En las dos, cuando una fase falla su crédito se devuelve automáticamente. Una consulta que no se puede completar, o de una patente que no figura registrada, no descuenta. Sin créditos suficientes, la API responde 402 quota_exceeded.

Pedidos repetidos

No se puede pedir una patente mientras haya otro pedido de la misma patente con una fase en curso que se superponga (por ejemplo, un dominio mientras otro dominio de esa patente espera a DNRPA). La API responde 409 duplicate_in_progress con el id del pedido existente en details.informeId. Cuando ese pedido termina se puede volver a pedir. Un pedido chasis sólo se bloquea mientras las multas de un dominio de esa patente siguen en curso; esperando a DNRPA ya no.

Avisos

Cuando termina una fase, Chasis envía un webhook a la URL configurada para esa fase y un email a los destinatarios de aviso de la cuenta.

La fase de multas se entrega a los 15 minutos si ya respondieron las fuentes principales, y si no, a la hora con lo obtenido hasta ese momento. Algunas fuentes pueden actualizar sus datos más tarde: mientras eso es posible, fases.multas.seguimiento es true y el resumen se va actualizando.

Flotas

Una flota es el listado de autos de tu empresa que Chasis actualiza solo, cada cierto tiempo. Agregás las patentes, elegís la periodicidad y, el día que toca, a las 7:00 (hora de Argentina), se pide un informe chasis de cada auto: multas, deuda de patentes, VTV, GNC y recalls, sin informe de dominio.

periodicidad dia Se actualiza
semanal 1 a 7 (1 = lunes) Ese día de cada semana
quincenal 1 a 13 Ese día y 15 días después, todos los meses (por ejemplo 5: los días 5 y 20)
mensual 1 a 28 Ese día de cada mes
bimestral 1 a 28 Ese día, cada 2 meses desde que se configuró
trimestral 1 a 28 Ese día, cada 3 meses desde que se configuró

La flota de una cuenta nueva arranca activa, mensual, con el día en que se usó por primera vez. Admite hasta 2.000 autos.


Autenticación

Todas las solicitudes llevan la API key en el header Authorization:

Authorization: Bearer chs_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

La key identifica la cuenta. Opcionalmente podés enviar Chasis-Account: <account_id>: si no coincide con la cuenta de la key, la solicitud se rechaza con 401. Sirve como resguardo cuando un mismo sistema maneja varias cuentas.

Permisos

Permiso Alcance Uso recomendado
Completo Todas las operaciones Backend que pide informes
Sólo lectura Sólo GET: créditos, cuenta, informes, PDFs, flota e historiales Dashboards, reportes, herramientas internas

Con una key de sólo lectura, cualquier otra operación responde 403 forbidden.

Administración de keys


Convenciones

Errores

Los errores responden con un código HTTP y este cuerpo:

{
  "error": {
    "code": "quota_exceeded",
    "message": "Cupo mensual de informes dominio agotado (100/100)",
    "requestId": "req_8Hc2kQ...",
    "details": { "tipo": "dominio", "used": 100, "quota": 100, "period": "2026-10" }
  }
}

Programá contra code, no contra message (el texto puede cambiar).

HTTP code Causa y acción
400 invalid_request Parámetros inválidos. details indica campo y motivo
401 unauthorized Key ausente, inválida o revocada, o Chasis-Account no coincide
402 quota_exceeded Sin créditos de ese tipo (cupo del mes agotado o saldo en cero)
403 forbidden Key de sólo lectura en una operación de escritura, u operación que sólo se permite desde el panel
403 client_suspended Cuenta suspendida. Contactar a soporte
404 not_found El recurso no existe o pertenece a otra cuenta. En POST /consultas: la patente no figura registrada (no se descuenta)
409 duplicate_in_progress Ya hay un pedido en curso de esa patente. details.informeId es el existente. En consultas: otra consulta de la misma patente sigue en curso; reintentar en unos segundos
409 idempotency_conflict Idempotency-Key reutilizada con otro cuerpo
409 not_ready El PDF todavía no está disponible
413 payload_too_large Cuerpo mayor a 16 KB
415 unsupported_media_type Falta Content-Type: application/json
429 rate_limited, poll_too_fast, polling_window_exceeded Esperar los segundos de Retry-After
503 upstream_unavailable Servicio de origen no disponible. No se consumió crédito; reintentar

Límites

Límite Valor
Solicitudes por API key 120 por minuto
Informes nuevos por cuenta 30 por minuto
Consultas Dentro del límite por API key
Detalle de un mismo informe 1 consulta cada 10 segundos

Ante un 429, respetá el header Retry-After.

Idempotencia

POST /informes acepta el header Idempotency-Key (8 a 128 caracteres [A-Za-z0-9_-], válido por 24 horas). Si repetís la solicitud con la misma key, recibís el mismo informe con 200 y el header Idempotent-Replayed: true, sin crear otro ni consumir créditos. La misma key con un cuerpo distinto responde 409 idempotency_conflict.

Recomendación: generá la key una vez por pedido del usuario (por ejemplo, al abrir el formulario) y reutilizala en todos los reintentos de ese pedido.

Paginación

Los listados devuelven { "object": "list", "data": [...], "hasMore": true, "nextCursor": "..." }. Para la página siguiente, enviá cursor=<nextCursor>. Los cursores son opacos.

Trazabilidad

Cada respuesta incluye X-Request-Id. Registralo en tus logs y incluilo en las consultas a soporte.

Versionado

La versión forma parte de la URL (/v1). Dentro de una versión sólo se hacen cambios compatibles: campos nuevos en las respuestas, endpoints nuevos y códigos de error nuevos. Tu integración tiene que ignorar los campos que no conoce.


Referencia

Créditos — GET /usage

curl https://www.chasis.com.ar/api/v1/usage -H "Authorization: Bearer $CHASIS_API_KEY"
{
  "object": "usage",
  "billing": "mensual",
  "period": "2026-10",
  "chasis":   { "quota": 500,  "used": 42,  "remaining": 458 },
  "dominio":  { "quota": 100,  "used": 7,   "remaining": 93 },
  "consulta": { "quota": 5000, "used": 812, "remaining": 4188 }
}

En una cuenta con billing: "creditos", quota es null, remaining es el saldo disponible (no vence) y used lo consumido en el mes:

{
  "object": "usage",
  "billing": "creditos",
  "period": "2026-10",
  "chasis":   { "quota": null, "used": 12,  "remaining": 38 },
  "dominio":  { "quota": null, "used": 3,   "remaining": 17 },
  "consulta": { "quota": null, "used": 150, "remaining": 850 }
}

Cuenta — GET /account

{
  "object": "account",
  "accountId": "Ab3dEf6hIj9kLm2nOp",
  "name": "ACME SA",
  "email": "compras@acme.com.ar",
  "status": "active",
  "billing": "mensual",
  "plan": { "id": "p1", "name": "Pyme 100" },
  "usage": {
    "period": "2026-10",
    "chasis":   { "quota": 500,  "used": 42,  "remaining": 458 },
    "dominio":  { "quota": 100,  "used": 7,   "remaining": 93 },
    "consulta": { "quota": 5000, "used": 812, "remaining": 4188 }
  },
  "notifications": { "multas": true, "dominio": true, "emails": ["compras@acme.com.ar"] },
  "webhooks": { "multas": "https://acme.com/chasis/webhook", "dominio": "https://acme.com/chasis/webhook", "secretConfigured": true }
}

Consultar una patente — POST /consultas

curl -X POST https://www.chasis.com.ar/api/v1/consultas \
  -H "Authorization: Bearer $CHASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"patente":"AB123CD"}'
Campo Requerido Descripción
patente Sí ABC123 o AB123CD. Se ignoran espacios, puntos y guiones; se normaliza a mayúsculas

Respuesta: 200 OK con el objeto Consulta, en general en menos de 10 segundos. cobrada indica si esta llamada descontó una consulta y reutilizada, si se devolvió lo guardado de los últimos 15 días.

Errores frecuentes: 400 invalid_request, 402 quota_exceeded, 404 not_found (la patente no figura registrada; no se descuenta), 409 duplicate_in_progress, 429 rate_limited, 503 upstream_unavailable (no se descuenta; reintentar).

Requiere una API key con permiso completo: las de sólo lectura no pueden consultar porque la consulta consume créditos.

Listar consultas — GET /consultas

Parámetro Descripción
patente Devuelve sólo la consulta de esa patente (vacío si nunca se consultó)
limit 1 a 100 (por defecto 25)
cursor nextCursor de la página anterior

Devuelve las Consultas de la cuenta, de la última pedida a la más vieja, con ultimaVez (la última vez que se pidió). No descuenta créditos.

Crear un informe — POST /informes

curl -X POST https://www.chasis.com.ar/api/v1/informes \
  -H "Authorization: Bearer $CHASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4b7e1c2a-pedido-8812" \
  -d '{"patente":"AB123CD","tipo":"dominio","reference":"pedido-8812"}'
Campo Requerido Descripción
patente Sí ABC123 o AB123CD. Se ignoran espacios, puntos y guiones; se normaliza a mayúsculas
tipo No dominio o chasis. Por defecto completo, que equivale a dominio
provincia No Provincia de radicación, si se conoce (por ejemplo "Córdoba")
reference No Identificador propio, hasta 128 caracteres [A-Za-z0-9_.:-]. Se devuelve en el informe y en los webhooks

Respuesta: 202 Accepted con el objeto Informe (sin resumen) y el header Location.

Errores frecuentes: 400 invalid_request, 402 quota_exceeded, 409 duplicate_in_progress, 429 rate_limited, 503 upstream_unavailable.

Listar informes — GET /informes

curl "https://www.chasis.com.ar/api/v1/informes?dias=30&limit=25" \
  -H "Authorization: Bearer $CHASIS_API_KEY"
Parámetro Descripción
patente Filtra por patente
dias Informes de los últimos N días
tipo dominio, chasis o completo (ver arriba)
status pending, processing, completed o failed
limit 1 a 100 (por defecto 25)
cursor nextCursor de la página anterior

Devuelve una lista de Informes sin resumen, del más reciente al más antiguo. No tiene límite propio de consultas (aplica el general), así que es el endpoint indicado para refrescar estados.

Obtener un informe — GET /informes/{id}

Devuelve el Informe completo, con resumen.

Máximo una consulta cada 10 segundos por informe (429 poll_too_fast). Si el informe todavía no tiene ninguna fase terminada, el detalle se puede consultar durante 75 minutos desde la primera vez (las multas se entregan dentro de la primera hora); después responde 429 polling_window_exceeded hasta que termine una fase. El dominio puede tardar horas: para seguirlo, usá el listado o los webhooks.

Descargar PDFs

Endpoint Contenido Disponible cuando
GET /informes/{id}/pdf Reporte de Chasis en formato técnico: multas por jurisdicción, deuda de patentes y, al final, vehículo, radicación, VTV, GNC, recalls e informe de dominio Terminó la fase de multas
GET /informes/{id}/pretty-pdf El mismo informe con el diseño de Chasis: imagen del auto, resumen y una sección por fuente. Pensado para mostrárselo a tu cliente final Terminó la fase de multas. La primera descarga puede tardar hasta un minuto
GET /informes/{id}/dominio.pdf PDF original emitido por DNRPA fases.dominio.pdfDisponible es true

Responden application/pdf con Content-Disposition: attachment. Si el documento todavía no está disponible, 409 not_ready. Los PDFs de DNRPA se conservan sin vencimiento.

curl -o informe.pdf https://www.chasis.com.ar/api/v1/informes/{id}/pdf \
  -H "Authorization: Bearer $CHASIS_API_KEY"

Configurar avisos — PATCH /account

Requiere una key con permiso completo.

{ "notifications": { "multas": true, "dominio": false } }

Activa o desactiva los emails de aviso de cada fase. Los destinatarios los administra Chasis; pedí cambios a soporte. Devuelve el objeto Cuenta actualizado.

Webhooks y secret de firma

Las URLs de webhook (una por fase) y el secret de firma se configuran sólo desde el panel, en https://www.chasis.com.ar/cuenta/ajustes. Por seguridad no se pueden modificar con una API key: si una key se filtra, no permite redirigir los informes. Cada cambio se notifica por email al titular de la cuenta.

GET /account devuelve las URLs configuradas en webhooks.

Probar el webhook — POST /account/webhook-test

Envía un evento webhook.test y responde:

{ "deliveryId": "whd_...", "status": "delivered", "lastStatusCode": 200, "lastError": null }

status es delivered si tu servidor respondió 2xx, o failed en otro caso. lastStatusCode es el código HTTP que respondió tu servidor (null si no se pudo conectar).

Historial de una patente — GET /vehiculos/{patente}

Todos los informes de una patente en tu cuenta, del más nuevo al más viejo: los de la flota y los que pediste por la API o el panel. Funciona aunque el auto nunca haya estado en la flota o ya se haya quitado. Cada informe viene completo, con su resumen, y con cambios: lo que cambió respecto del informe anterior de la misma patente.

curl https://www.chasis.com.ar/api/v1/vehiculos/AB123CD \
  -H "Authorization: Bearer $CHASIS_API_KEY"
{
  "object": "vehiculo",
  "patente": "AB123CD",
  "vehiculo": { "marca": "FIAT", "modelo": "CRONOS DRIVE 1.3", "anio": "2023" },
  "flota": { "activo": true, "alias": "Camioneta 3", "reference": "int-3", "provincia": null, "agregadoEn": "2026-10-01T12:00:00.000Z", "quitadoEn": null, "ultimaActualizacion": "2026-11-01T10:20:31.000Z" },
  "ultimoInforme": { "id": "Xk2pQ9...", "status": "completed", "createdAt": "2026-11-01T10:20:31.000Z", "links": { } },
  "informes": {
    "object": "list",
    "data": [
      {
        "id": "Xk2pQ9...", "object": "informe", "tipo": "chasis", "status": "completed",
        "flota": { "ejecucionId": "fej_..." },
        "resumen": { },
        "cambios": {
          "hay": true,
          "multasNuevas": [ { "jurisdiccion": "CABA", "fecha": "12/10/2026", "monto": 98400, "descripcion": "Exceso de velocidad" } ],
          "multasQueNoFiguran": 1,
          "lineas": ["1 multa nueva por $98.400", "1 multa del informe anterior ya no figura"]
        }
      }
    ],
    "hasMore": false,
    "nextCursor": null
  }
}
Parámetro Descripción
limit Informes por página, 1 a 100 (por defecto 25)
cursor nextCursor de la página anterior

Cada informe incluye además links.historial con esta ruta.

Flota — GET /flota

{
  "object": "flota",
  "activa": true,
  "periodicidad": "mensual",
  "dia": 1,
  "proximaEjecucion": "2026-11-01",
  "vehiculos": 48,
  "maxVehiculos": 2000,
  "ultimaEjecucion": { "object": "ejecucion", "id": "fej_...", "estado": "terminada" },
  "links": { "vehiculos": "/api/v1/flota/vehiculos", "ejecuciones": "/api/v1/flota/ejecuciones" }
}

proximaEjecucion es el próximo día que se actualiza (YYYY-MM-DD, hora de Argentina), o null si la flota está pausada. ultimaEjecucion es la última actualización programada o manual (ver El objeto Ejecución).

Configurar la flota — PATCH /flota

Requiere una key con permiso completo.

curl -X PATCH https://www.chasis.com.ar/api/v1/flota \
  -H "Authorization: Bearer $CHASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"periodicidad":"quincenal","dia":5}'
Campo Descripción
periodicidad semanal, quincenal, mensual, bimestral o trimestral (ver Flotas)
dia Día en que se actualiza, según la periodicidad. Si cambiás la periodicidad sin mandar dia, se toma el de hoy
activa false pausa las actualizaciones (los autos y su historial se conservan); true las reanuda

Devuelve el objeto Flota. Un dia fuera de rango responde 400 invalid_request.

Autos de la flota — GET /flota/vehiculos

El listado completo de la flota. Cada auto trae lo esencial de su último informe; el informe completo está en su detalle o en GET /informes/{id}.

{
  "object": "list",
  "data": [
    {
      "object": "vehiculo_flota",
      "patente": "AB123CD",
      "alias": "Camioneta 3",
      "reference": "int-3",
      "provincia": null,
      "activo": true,
      "agregadoEn": "2026-10-01T12:00:00.000Z",
      "quitadoEn": null,
      "ultimaActualizacion": "2026-11-01T10:20:31.000Z",
      "vehiculo": { "marca": "FIAT", "modelo": "CRONOS DRIVE 1.3", "anio": "2023" },
      "ultimoInforme": {
        "id": "Xk2pQ9...",
        "status": "completed",
        "createdAt": "2026-11-01T10:20:31.000Z",
        "completedAt": "2026-11-01T10:41:02.000Z",
        "multas": { "cantidad": 5, "montoTotal": 1689366 },
        "patentes": { "tieneDeuda": false, "montoTotal": 0 },
        "vtv": { "vigente": false, "fuente": "vtv_caba" },
        "links": { "self": "/api/v1/informes/Xk2pQ9..." }
      },
      "links": { "self": "/api/v1/flota/vehiculos/AB123CD", "historial": "/api/v1/vehiculos/AB123CD" }
    }
  ],
  "total": 48,
  "hasMore": false,
  "nextCursor": null
}
Parámetro Descripción
limit 1 a 500 (por defecto 100)
cursor nextCursor de la página anterior
q Busca en la patente, el alias y reference
incluirQuitados true incluye los autos que se quitaron de la flota (activo: false)

ultimoInforme es null si el auto todavía no tiene informes. Sus bloques (multas, patentes, vtv) son null mientras las multas siguen en proceso.

Agregar autos — POST /flota/vehiculos

Requiere una key con permiso completo. Hasta 500 autos por solicitud.

curl -X POST https://www.chasis.com.ar/api/v1/flota/vehiculos \
  -H "Authorization: Bearer $CHASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"vehiculos":["AB123CD",{"patente":"AC456EF","alias":"Camioneta 3","reference":"int-3"}],"informeInicial":true}'
Campo Requerido Descripción
vehiculos Sí Patentes, o objetos con patente y opcionalmente alias (hasta 80 caracteres), reference (tu identificador, como en POST /informes) y provincia
informeInicial No true pide ya un informe de los autos nuevos (1 crédito chasis cada uno). Por defecto false: se actualizan la próxima fecha
{
  "object": "flota_alta",
  "agregados": ["AC456EF"],
  "reactivados": [],
  "yaEstaban": ["AB123CD"],
  "ejecucion": { "object": "ejecucion", "id": "fej_...", "tipo": "alta", "estado": "en_curso" },
  "flota": { "object": "flota" }
}

Un auto de la flota — GET /flota/vehiculos/{patente}

El auto con su historial completo: mismo formato y parámetros que GET /vehiculos/{patente}. Funciona también con los autos que se quitaron de la flota; si la patente nunca estuvo en la flota, 404 not_found.

Editar un auto — PATCH /flota/vehiculos/{patente}

Requiere una key con permiso completo. Cambia alias, reference o provincia (null los borra). Devuelve el auto con su historial.

Quitar un auto — DELETE /flota/vehiculos/{patente}

Requiere una key con permiso completo. El auto deja de actualizarse. Sus informes y su historial se conservan y se pueden seguir consultando por patente. Para volver a agregarlo, POST /flota/vehiculos.

{ "object": "vehiculo_flota", "patente": "AB123CD", "activo": false, "quitadoEn": "2026-11-03T15:02:11.000Z", "links": { "historial": "/api/v1/vehiculos/AB123CD" } }

Si el auto no está en la flota, 404 not_found.

Actualizaciones de la flota — GET /flota/ejecuciones

Las actualizaciones (programadas, manuales y los informes iniciales de autos agregados), de la más reciente a la más antigua. limit de 1 a 100 (por defecto 25). El detalle de una: GET /flota/ejecuciones/{id}.

Actualizar la flota ahora — POST /flota/ejecuciones

Requiere una key con permiso completo. Pide ya un informe de cada auto activo (1 crédito chasis cada uno), además de las fechas programadas. Responde 202 con el objeto Ejecución. Si ya hay una actualización en curso, 409 duplicate_in_progress con su id en details.ejecucionId.


Objetos

El objeto Informe

{
  "id": "Xk2pQ9...",
  "object": "informe",
  "accountId": "Ab3dEf6hIj9kLm2nOp",
  "tipo": "dominio",
  "patente": "AB123CD",
  "status": "processing",
  "fases": {
    "multas":  { "status": "completed",  "completedAt": "2026-10-14T18:09:12.000Z", "error": null, "seguimiento": true, "actualizadoEn": null },
    "dominio": { "status": "processing", "completedAt": null, "error": null, "pdfDisponible": false }
  },
  "source": "api",
  "flota": null,
  "reference": "pedido-8812",
  "vehiculo": { "marca": "VOLKSWAGEN", "modelo": "GOL TREND", "anio": "2018" },
  "createdAt": "2026-10-14T18:01:03.000Z",
  "updatedAt": "2026-10-14T18:09:12.000Z",
  "completedAt": null,
  "error": null,
  "links": {
    "self": "/api/v1/informes/Xk2pQ9...",
    "pdf": "/api/v1/informes/Xk2pQ9.../pdf",
    "prettyPdf": "/api/v1/informes/Xk2pQ9.../pretty-pdf",
    "dominioPdf": null,
    "historial": "/api/v1/vehiculos/AB123CD"
  },
  "resumen": { }
}
Campo Tipo Descripción
id string Identificador del informe
accountId string Cuenta dueña del informe
tipo string dominio, chasis o completo (lo que se pidió)
patente string Patente normalizada
status string Estado general (ver Estados)
fases object Estado de cada fase pedida. error tiene code y message cuando la fase falló
fases.dominio.pdfDisponible boolean El PDF original de DNRPA está disponible
fases.multas.seguimiento boolean Las multas ya se entregaron, pero pueden actualizarse en las próximas horas; cada actualización llega como multas.updated
fases.multas.actualizadoEn string | null Última actualización de las multas posterior a la entrega
source string api, panel o chasis (cargado por el equipo de Chasis)
flota object | null { "ejecucionId": "fej_..." } si lo pidió una actualización de la flota; null en el resto. Los informes de la flota tienen source: "api"
reference string | null Tu identificador, si lo enviaste
vehiculo object | null Marca, modelo y año, cuando ya se conocen
links object Rutas relativas a https://www.chasis.com.ar. historial lleva a todos los informes de la patente
resumen object | null Sólo en el detalle y en los webhooks. Ver abajo

El objeto Consulta

{
  "object": "consulta",
  "patente": "AB123CD",
  "dominio": "AB123CD",
  "marca": "FIAT",
  "modelo": "CRONOS DRIVE 1.3 GSE CVT BZ",
  "anio": 2023,
  "tipo": "automotor",
  "placaMercosur": true,
  "registro": {
    "codigo": 1234,
    "nombre": "CORDOBA N° 5",
    "direccion": "AV. COLON 1234",
    "localidad": "CORDOBA",
    "provincia": "Córdoba"
  },
  "consultadoEn": "2026-10-20T14:03:11.000Z",
  "vigenteHasta": "2026-11-04T14:03:11.000Z",
  "cobrada": true,
  "reutilizada": false
}
Campo Descripción
marca, modelo, anio Según DNRPA. modelo incluye la versión
tipo automotor, moto o null
placaMercosur Si la patente es del formato Mercosur
registro Registro seccional de radicación. Cualquiera de sus campos puede venir en null
consultadoEn Cuándo se consultó DNRPA
vigenteHasta Hasta cuándo una consulta repetida devuelve estos datos sin descontar
cobrada, reutilizada Sólo en POST /consultas

El objeto Resumen

Contiene la información lista para mostrar. Es la estructura recomendada para cualquier interfaz.

{
  "patente": "AB123CD",
  "vehiculo": { "marca": "VOLKSWAGEN", "modelo": "GOL TREND", "anio": "2018", "version": null },
  "multas": {
    "cantidad": 3,
    "montoTotal": 245000,
    "lista": [
      {
        "entity": { "name": "CABA", "province": "CABA" },
        "infracted_at": "2026-03-02T14:10:00Z",
        "unique_id": "Q12345678",
        "description": "Exceso de velocidad",
        "amount": 120000,
        "expires_at": "2026-11-01",
        "requires_controller": false,
        "resolved_at": null,
        "location": "AV. LUGONES Y ..."
      }
    ],
    "conMultas": [ { "id": "caba", "nombre": "CABA", "provincia": "CABA" } ]
  },
  "patentes": {
    "tieneDeuda": true,
    "montoTotal": 58000,
    "detalle": [ { "jurisdiccion": "CABA", "periodos": ["2026-01", "2026-02"], "monto": 58000 } ],
    "consultadas": ["CABA", "BUENOS AIRES"]
  },
  "vtv": { "vigente": true, "fuente": "vtv_provincia" },
  "gnc": { "tiene": false },
  "recalls": { "tiene": false, "cantidad": 0 },
  "dominio": {
    "marca": "VOLKSWAGEN",
    "modelo": "GOL TREND",
    "anio": "2018",
    "nroChasis": "9BWAB45U0JT000000",
    "nroMotor": "CFZ000000",
    "titularesCantidad": 1,
    "titulares": [ { "nombre": "PEREZ JUAN", "porcentaje": "100%", "documento": "********123" } ],
    "estadosActivos": [ { "key": "prenda", "label": "Prenda" } ],
    "fechaInscripcion": "2018-05-10",
    "radicacion": "CABA"
  }
}
Bloque Descripción
multas Multas abiertas (sin pagar ni resolver) de todas las jurisdicciones consultadas, monto total en pesos y jurisdicciones con multas
patentes Deuda de patentes por jurisdicción y jurisdicciones consultadas
vtv, gnc, recalls Indicadores; null en un campo indica que no se pudo determinar
dominio Datos del informe de DNRPA. Los documentos de los titulares van enmascarados (últimos 3 dígitos). estadosActivos lista gravámenes y restricciones vigentes (prenda, embargo, inhibición, denuncia de robo, etc.)

Los bloques aparecen recién cuando terminó su fase: multas, patentes, vtv, gnc y recalls cuando termina la fase de multas, y dominio cuando llega el informe de DNRPA. Si después cambian las multas, el resumen se actualiza y se envía multas.updated. Un bloque en null significa que la fase no se pidió, todavía no terminó o la fuente no aplica: en una interfaz, mostralo como "en proceso" o "no disponible", nunca como ausencia de multas o de deuda.

El objeto Ejecución

Una actualización de la flota.

{
  "object": "ejecucion",
  "id": "fej_...",
  "tipo": "programada",
  "fecha": "2026-11-01",
  "estado": "terminada",
  "total": 48,
  "pedidos": 46,
  "pendientes": 0,
  "omitidos": [ { "patente": "AD789GH", "motivo": "sin_cupo", "detalle": "No quedaban créditos chasis" } ],
  "informes": [ { "patente": "AB123CD", "informeId": "Xk2pQ9...", "enCurso": false } ],
  "resumen": { "actualizados": 46, "conMultas": 12, "multas": 31, "montoMultas": 4210000, "conNovedades": 5, "sinCupo": 2, "errores": 0 },
  "createdAt": "2026-11-01T10:00:04.000Z",
  "terminadaEn": "2026-11-01T11:12:40.000Z"
}
Campo Descripción
tipo programada (la del día que toca), manual (POST /flota/ejecuciones) o alta (informeInicial al agregar autos)
estado en_curso: pidiendo los informes. esperando: todos pedidos, esperando que terminen las multas. terminada: se envió el email con el resumen
pedidos Autos con su informe pedido. informes los lista; enCurso: true indica que ya había un informe en curso de esa patente y se usó ese
pendientes Autos que todavía falta pedir
omitidos Autos que no se actualizaron: sin_cupo (sin créditos chasis) o error
resumen Sólo cuando está terminada: autos actualizados, cuántos con multas, total de multas y monto, cuántos con novedades respecto del informe anterior

Webhooks

Cuando termina una fase, Chasis envía un POST a la URL configurada para esa fase. Mientras las multas están en seguimiento (fases.multas.seguimiento), se envía multas.updated con el informe completo cada vez que cambia algo del resumen, y una última vez al terminar el seguimiento (con seguimiento: false). Cada multas.updated reemplaza al anterior: guardá siempre el último.

Evento Destino Contenido de data.informe
multas.completed webhooks.multas Informe completo, con resumen
multas.updated webhooks.multas Informe completo, con el resumen actualizado de multas
multas.failed webhooks.multas Informe completo; el motivo en fases.multas.error
dominio.completed webhooks.dominio Informe completo, incluido el dominio; links.dominioPdf disponible
dominio.failed webhooks.dominio Informe completo; el motivo en fases.dominio.error
webhook.test URL configurada data.message
POST https://acme.com/chasis/webhook
Content-Type: application/json
User-Agent: Chasis-Webhooks/1.0
Chasis-Event: dominio.completed
Chasis-Delivery: whd_...
Chasis-Account: Ab3dEf6hIj9kLm2nOp
Chasis-Signature: t=1760000000,v1=5f2c...

{ "id": "whd_...", "object": "event", "type": "dominio.completed", "createdAt": "...", "data": { "informe": { ... } } }

Verificación de la firma

Chasis-Signature tiene la forma t=<timestamp>,v1=<firma>. La firma es hex(HMAC-SHA256(secret, "<t>.<body>")), calculada sobre el cuerpo crudo, tal como llega, sin parsearlo ni volver a serializarlo.

  1. Separá t y todos los valores v1. Durante las 24 horas posteriores a una rotación del secret, el header trae dos v1; la firma es válida si coincide alguno.
  2. Rechazá el evento si |ahora − t| supera los 300 segundos.
  3. Compará en tiempo constante.
import crypto from "node:crypto";

export function verificarFirma(rawBody, header, secret) {
  let t;
  const firmas = [];
  for (const parte of (header ?? "").split(",")) {
    const [k, v] = parte.split("=");
    if (k === "t") t = Number(v);
    if (k === "v1" && /^[0-9a-f]{64}$/.test(v)) firmas.push(Buffer.from(v, "hex"));
  }
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const esperada = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return firmas.some((f) => crypto.timingSafeEqual(f, esperada));
}

El SDK incluye esta verificación (verificarWebhook).

Entrega


Cliente TypeScript

https://www.chasis.com.ar/sdk/chasis.ts es un único archivo sin dependencias, con tipos para todos los objetos. Funciona en Node 18+, Bun, Deno y runtimes edge. Es para uso en el servidor.

import { Chasis, ChasisError, verificarWebhook } from "./chasis";

const chasis = new Chasis({ apiKey: process.env.CHASIS_API_KEY! });

await chasis.uso();                                        // créditos del mes
await chasis.cuenta();                                     // cuenta, plan, avisos, webhooks
await chasis.consultar("AB123CD");                         // datos y radicación según DNRPA, al instante
await chasis.listarConsultas({ limit: 50 });
await chasis.crearInforme({ patente: "AB123CD", tipo: "dominio" });  // DNRPA + multas
await chasis.crearInforme({ patente: "AB123CD", tipo: "chasis" });   // sólo multas
await chasis.crearInforme({ patente: "AB123CD", tipo: "dominio", idempotencyKey });
await chasis.listarInformes({ patente: "AB123CD", dias: 30 });
for await (const informe of chasis.todosLosInformes({ dias: 90 })) { /* ... */ }
await chasis.informe(id);                                  // detalle, con resumen
await chasis.pdfInforme(id);                               // ArrayBuffer
await chasis.pdfLindo(id);                                 // ArrayBuffer, con el diseño de Chasis
await chasis.pdfDominio(id);                               // ArrayBuffer
await chasis.esperar(id, { fase: "multas" });              // espera a que terminen las multas
await chasis.historial("AB123CD");                         // todos los informes de la patente, con los cambios
await chasis.configurarFlota({ periodicidad: "mensual", dia: 1 });
await chasis.agregarAFlota(["AB123CD", { patente: "AC456EF", alias: "Camioneta 3" }], { informeInicial: true });
await chasis.listarFlota();                                // autos con su último informe
for await (const auto of chasis.todaLaFlota()) { /* ... */ }
await chasis.vehiculoDeFlota("AB123CD");                   // auto + historial
await chasis.quitarDeFlota("AB123CD");                     // deja de actualizarse; el historial queda
await chasis.actualizarFlota();                            // actualiza ya toda la flota
await chasis.ejecucionesDeFlota();
await chasis.configurarAvisos({ multas: false });
await chasis.probarWebhook();                              // envía un webhook.test
const evento = await verificarWebhook(rawBody, signatureHeader, process.env.CHASIS_WEBHOOK_SECRET!);

Integración paso a paso

Arquitectura recomendada: tu frontend nunca llama a Chasis. Llama a rutas de tu backend, que exigen un usuario autenticado de tu aplicación y son las únicas que conocen la API key. Los ejemplos usan Next.js (App Router); en otros frameworks la estructura es la misma.

1. Variables de entorno (servidor)

CHASIS_API_KEY=chs_live_...
CHASIS_WEBHOOK_SECRET=whsec_...

2. Cliente

Descargá https://www.chasis.com.ar/sdk/chasis.ts a lib/chasis.ts y creá una instancia única:

// lib/chasis-server.ts
import "server-only";
import { Chasis } from "./chasis";

export const chasis = new Chasis({ apiKey: process.env.CHASIS_API_KEY! });

Verificá la conexión con await chasis.cuenta(). Un 401 indica una key mal copiada o revocada.

3. Autenticación obligatoria en tus rutas

Todas las rutas que llaman a Chasis, salvo el receptor de webhooks, tienen que exigir una sesión válida de tu aplicación y rechazar la solicitud si no la hay. Sin esto, cualquiera podría consumir tus créditos y acceder a datos de titulares. Si tu aplicación no tiene autenticación, implementala antes de exponer estas rutas.

// lib/auth-guard.ts — adaptalo al sistema de sesión de tu aplicación.
import "server-only";

export async function requireUser(req: Request): Promise<{ id: string }> {
  const user = await getSessionUser(req); // tu función de sesión (NextAuth, Clerk, Supabase, Firebase...)
  if (!user) throw new Response("No autorizado", { status: 401 });
  return user;
}
// lib/chasis-route.ts — exige sesión y traduce los errores de Chasis.
import "server-only";
import { ChasisError } from "./chasis";
import { requireUser } from "./auth-guard";

export function ruta<C>(handler: (req: Request, ctx: C, user: { id: string }) => Promise<Response>) {
  return async (req: Request, ctx: C) => {
    try {
      const user = await requireUser(req);
      return await handler(req, ctx, user);
    } catch (e) {
      if (e instanceof Response) return e;
      if (e instanceof ChasisError) {
        return Response.json(
          { code: e.code, message: e.message, details: e.details ?? null },
          { status: e.status >= 400 ? e.status : 502 },
        );
      }
      console.error(e);
      return Response.json({ code: "internal_error", message: "Error interno" }, { status: 500 });
    }
  };
}

4. Rutas del backend

// app/api/chasis/creditos/route.ts
import { chasis } from "@/lib/chasis-server";
import { ruta } from "@/lib/chasis-route";

export const GET = ruta(async () => Response.json(await chasis.uso()));
// app/api/chasis/informes/route.ts
import { chasis } from "@/lib/chasis-server";
import { ruta } from "@/lib/chasis-route";

const TIPOS = ["dominio", "chasis"] as const;
const ESTADOS = ["pending", "processing", "completed", "failed"] as const;

export const GET = ruta(async (req) => {
  const sp = new URL(req.url).searchParams;
  const status = sp.get("status");
  return Response.json(
    await chasis.listarInformes({
      patente: sp.get("patente") || undefined,
      dias: sp.get("dias") ? Number(sp.get("dias")) : undefined,
      status: ESTADOS.find((s) => s === status),
      cursor: sp.get("cursor") || undefined,
      limit: 25,
    }),
  );
});

export const POST = ruta(async (req) => {
  const body = (await req.json().catch(() => ({}))) as { patente?: string; tipo?: string; idempotencyKey?: string };
  if (!body.patente) {
    return Response.json({ code: "invalid_request", message: "Falta la patente" }, { status: 400 });
  }
  const tipo = TIPOS.find((t) => t === body.tipo) ?? "dominio";
  // La genera el frontend una vez por pedido y la reenvía igual en cada reintento.
  const idempotencyKey = /^[A-Za-z0-9_-]{8,128}$/.test(body.idempotencyKey ?? "") ? body.idempotencyKey : undefined;
  const informe = await chasis.crearInforme({ patente: body.patente, tipo, idempotencyKey });
  return Response.json(informe, { status: 202 });
});
// app/api/chasis/informes/[id]/route.ts
import { chasis } from "@/lib/chasis-server";
import { ruta } from "@/lib/chasis-route";

export const GET = ruta<{ params: Promise<{ id: string }> }>(async (_req, { params }) => {
  return Response.json(await chasis.informe((await params).id));
});
// app/api/chasis/informes/[id]/pdf/route.ts — ?tipo=dominio para el PDF de DNRPA.
import { chasis } from "@/lib/chasis-server";
import { ruta } from "@/lib/chasis-route";

export const GET = ruta<{ params: Promise<{ id: string }> }>(async (req, { params }) => {
  const { id } = await params;
  const dominio = new URL(req.url).searchParams.get("tipo") === "dominio";
  const pdf = dominio ? await chasis.pdfDominio(id) : await chasis.pdfInforme(id);
  const nombre = `${dominio ? "dominio" : "informe"}-${id.replace(/[^A-Za-z0-9_-]/g, "")}.pdf`;
  return new Response(pdf, {
    headers: {
      "Content-Type": "application/pdf",
      "Content-Disposition": `attachment; filename="${nombre}"`,
      "Cache-Control": "private, no-store",
    },
  });
});

Chasis separa los datos por cuenta, no por usuario de tu aplicación. Si no todos tus usuarios deben ver todos los informes, guardá en tu base qué usuario pidió cada id y verificalo en el detalle y en los PDFs.

En el frontend, generá la clave de idempotencia al abrir el formulario y reutilizala en los reintentos:

const [idempotencyKey] = useState(() => crypto.randomUUID());

await fetch("/api/chasis/informes", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ patente, tipo, idempotencyKey }),
});

5. Interfaz

Pantalla Fuente Contenido
Créditos GET /usage remaining para chasis y dominio (cupo del mes o saldo, según billing). Deshabilitá "Pedir" si no alcanza: chasis usa un crédito de chasis; dominio (o completo), uno de dominio
Pedir informe POST /informes Patente y tipo. Validá ^[A-Z]{3}\d{3}$ o ^[A-Z]{2}\d{3}[A-Z]{2}$ tras pasar a mayúsculas y quitar espacios. Ante duplicate_in_progress, ofrecé abrir details.informeId
Listado GET /informes Patente, vehículo, tipo, estado de cada fase y fecha. Filtros por patente y por días; paginación con nextCursor
Detalle GET /informes/{id} Bloques de resumen. Fases en curso como "En proceso"; el dominio, como "Esperando a DNRPA (días hábiles, 8 a 17 h)". PDFs habilitados según su disponibilidad

Estados sugeridos: processing → "En proceso", completed → "Listo", failed → "No disponible", con fases.<fase>.error.message.

Para mantener los estados actualizados, procesá los webhooks (paso 6) o refrescá el listado cada 30 a 60 segundos mientras haya informes en curso.

6. Receptor de webhooks

Esta ruta no exige sesión: la protege la firma.

// app/api/chasis/webhook/route.ts
import { verificarWebhook } from "@/lib/chasis";

export async function POST(req: Request) {
  const raw = await req.text(); // cuerpo crudo
  const evento = await verificarWebhook(raw, req.headers.get("chasis-signature"), process.env.CHASIS_WEBHOOK_SECRET!);
  if (!evento) return new Response("Firma inválida", { status: 401 });

  // Idempotencia: registrá Chasis-Delivery y descartá los repetidos.
  const informe = evento.data.informe;
  switch (evento.type) {
    case "multas.completed":
    case "multas.updated":
      // Guardar informe.resumen (reemplaza al anterior) y notificar al usuario.
      break;
    case "dominio.completed":
      // Guardar el informe completo; links.dominioPdf ya está disponible.
      break;
    case "multas.failed":
    case "dominio.failed":
      // Marcar como no disponible con informe.fases[...].error.message.
      break;
  }
  return new Response("ok");
}

Configuración inicial, desde https://www.chasis.com.ar/cuenta/ajustes:

  1. Cargá la URL del receptor (por ejemplo https://tu-dominio.com/api/chasis/webhook) para las fases de multas y de dominio.
  2. Generá el secret de firma y guardalo en CHASIS_WEBHOOK_SECRET.
  3. Enviá una prueba ("Enviar prueba" en el panel, o await chasis.probarWebhook()) y verificá que el resultado sea delivered.

7. Verificación


Seguridad