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):
mensual: un cupo por mes según el plan, que se renueva el día 1 de cada mes (hora de Argentina).creditos: un saldo prepago que no vence; cada informe lo descuenta.
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.
- Webhook:
multas.completedal entregar la fase; después,multas.updatedcada vez que cambia algo del resumen (multas, deuda, VTV, GNC, recalls) y una última vez cuando termina el seguimiento. Con los webhooks tenés siempre el informe al día sin consultar la API. - Email: uno al entregar la fase. Después, uno de actualización únicamente si las multas cambian al terminar el seguimiento.
- Flotas: los informes de la flota avisan por webhook como cualquier otro; por email llega uno solo por actualización de la flota, con todos los autos (ver Flotas).
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.
- Cada informe de la flota es un informe común: descuenta 1 crédito
chasis, aparece enGET /informes, tiene sus PDFs y avisa por webhook (multas.completed,multas.updated) como cualquier otro. Se distingue por el campoflota. - Por email no llega un aviso por auto: llega uno solo por actualización, cuando terminan todos los autos, con las multas de cada uno y las novedades respecto de la actualización anterior.
- Si en una actualización se acaban los créditos, los autos que faltan quedan sin actualizar (figuran en
omitidoscon motivosin_cupo) y se actualizan en la próxima fecha. - Si ya hay un pedido en curso de esa patente (por ejemplo, uno que hiciste por la API), no se pide otro: la actualización usa ese.
- Quitar un auto de la flota sólo deja de actualizarlo. Sus informes siguen siendo tuyos y el historial de la patente los muestra todos, igual que los de cualquier patente que hayas consultado.
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
- Las keys se crean y revocan en
https://www.chasis.com.ar/cuenta/ajustes. Se muestran una sola vez. - Podés tener hasta 5 activas a la vez, para rotarlas sin cortar el servicio o separar sistemas.
- Las API keys no pueden crear ni revocar otras keys, ni cambiar los webhooks o su secret: eso se hace sólo desde el panel, con el login de la cuenta. Así, una key filtrada no permite redirigir los informes a otro destino.
- Cada vez que se crea o revoca una key, cambian los webhooks o se rota el secret de firma, se envía un email al titular de la cuenta.
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.
- URLs: sólo
https, con un nombre de host público, en el puerto 443. Podés usar la misma para las dos fases y distinguir por el tipo de evento. - Secret: se muestra una sola vez al generarlo. Al rotarlo, el anterior sigue siendo válido durante 24 horas: en ese período los webhooks llevan las dos firmas, para que lo actualices sin perder eventos.
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 |
flotaesnullsi la patente nunca estuvo en la flota.cambiosesnullcuando no hay un informe anterior con las multas terminadas para comparar, o cuando este todavía no las terminó. Sólo se compara lo que los dos informes saben: si una fuente no respondió en alguno, ese dato no cuenta como cambio.- Si la cuenta no tiene informes de esa patente y nunca estuvo en la flota, responde
404 not_found.
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" }
}
- Responde
201si agregó algún auto y200si ya estaban todos. Repetir la solicitud no duplica autos: es seguro reintentarla. - Un auto que ya estaba conserva sus datos, salvo los campos que mandes. Uno que se había quitado vuelve a la flota (
reactivados). - Si la patente ya tenía informes en la cuenta, el auto arranca con el último y la próxima actualización se compara contra ese.
ejecucionsólo aparece coninformeInicial: true(ver El objeto Ejecución).
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.
- Separá
ty todos los valoresv1. Durante las 24 horas posteriores a una rotación del secret, el header trae dosv1; la firma es válida si coincide alguno. - Rechazá el evento si
|ahora − t|supera los 300 segundos. - 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
- Respondé con un
2xxen menos de 10 segundos y procesá el evento de forma asíncrona. - Si la respuesta no es
2xx, se reintenta a 1 min, 5 min, 30 min, 2 h, 6 h y 12 h. - Un mismo evento puede llegar más de una vez. Deduplicá por
Chasis-Delivery, o pordata.informe.idmástype. - Las entregas no siguen redirecciones.
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!);
- Los errores se lanzan como
ChasisError, constatus,code,message,requestId,retryAfterydetails. - Reintenta automáticamente sólo ante
429,503y errores de red, respetandoRetry-After. crearInformeenvía siempre unaIdempotency-Key: si no la pasás, genera una por llamada, de modo que sus propios reintentos nunca consumen créditos dos veces. Para cubrir los reintentos de tu aplicación, pasá la tuya.
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:
- Cargá la URL del receptor (por ejemplo
https://tu-dominio.com/api/chasis/webhook) para las fases de multas y de dominio. - Generá el secret de firma y guardalo en
CHASIS_WEBHOOK_SECRET. - Enviá una prueba ("Enviar prueba" en el panel, o
await chasis.probarWebhook()) y verificá que el resultado seadelivered.
7. Verificación
-
cuenta()devuelve la cuenta. - Sin sesión, todas las rutas
/api/chasis/*salvo el webhook responden401. - La API key no aparece en el bundle del frontend ni en los logs.
- La pantalla de créditos muestra los saldos de chasis y dominio.
- Un pedido
chasisaparece en el listado en curso y, dentro de la primera hora, la fase de multas pasa acompletedcon su resumen y su PDF. - Un pedido
dominioentrega primero las multas y después el dominio (dominio.completed), con el PDF de DNRPA. - Repetir un pedido en curso devuelve
duplicate_in_progresscon el informe existente. - Patente inválida (
400) y falta de créditos (402) muestran un mensaje claro. -
probarWebhook()devuelvedelivered, y un webhook con la firma alterada se rechaza con401. - Un doble click en "Pedir" no genera dos pedidos.
Seguridad
- La API key sólo en el servidor. Nunca en código de navegador, apps móviles, repositorios ni logs. Ante una filtración, revocala desde el panel y creá otra.
- Mínimo privilegio. Usá keys de sólo lectura donde no haga falta pedir informes, y una key por sistema para poder revocarlas por separado.
- Webhooks. Verificá siempre la firma, rechazá eventos con más de 5 minutos de antigüedad y deduplicá.