# El precio de mercado de cualquier carro, dentro de tu sistema Manda año, marca, modelo, versión, kilómetros y estado. Recibe el precio en dólares, su rango y cada uno de los comparables que lo respaldan. Y recibe el resultado de las Inspecciones 360 de tus vehículos, con su informe en PDF. ## En pocas palabras - **15.000+**: comparables de Conduce y del mercado venezolano en la base. Cada precio usa los de su modelo. - **Uno por uno**: cada comparable del cálculo, con su año, su precio y sus kilómetros cuando la publicación los trae. - **Carros y motos**: con el mismo motor de Precio Real que usa la app. - **68 puntos**: en cada Inspección 360, con su informe en PDF. ## Empieza en tres pasos De la llave a tu primer precio. ### Pide tu llave Tu llave secreta te la entrega Conduce. Guárdala en una variable de entorno de tu servidor, `CONDUCE_API_KEY`, y nunca en una app ni en una página web. ```bash export CONDUCE_API_KEY="cnd_live_..." ``` ### Pide tu primera valuación Manda los datos del vehículo a `POST /v1/valuations`. Marca, modelo y versión van como en el catálogo; si no los tienes a mano, pídelos a `/v1/brands`, `/v1/models` y `/v1/versions`. Con `reference` amarras la valuación a tu conductor o a su expediente, para buscarla después. ```bash curl https://api.conduceapp.com/v1/valuations \ -H "Authorization: Bearer $CONDUCE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "vehicle_type": "car", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000, "condition": "good", "reference": "yummer-48213" }' ``` ### Lee la respuesta `price` trae el precio estimado y su rango en dólares. `confidence` te dice qué tan sólido es: cuántos comparables entraron y si fueron de la misma versión o de todo el modelo. En `comparables` viene cada vehículo que respalda el precio, con su precio y el ajustado al tuyo. Así puedes mostrarlo o revisarlo. Si `status` es `insufficient_data`, todavía no hay vehículos parecidos para dar un precio, y esa consulta no gasta cupo. ```json { "id": "val_7Qm2xK9pT4sW8vLd", "object": "valuation", "status": "complete", "vehicle": { "type": "car", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000, "condition": "good" }, "price": { "estimated": 24589, "low": 22622, "high": 26556, "currency": "USD" }, "confidence": { "level": "high", "match": "version", "comparables_used": 37 }, "comparables": [ { "source": "market", "status": "listed", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 80000, "price": 25081, "adjusted_price": 24581, "outlier": false }, { "source": "conduce", "status": "active", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 73000, "price": 28900, "adjusted_price": 27700, "outlier": false }, { "source": "market", "status": "listed", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 70000, "price": 23179, "adjusted_price": 21679, "outlier": false } // … y los otros 34 comparables ], "message": null, "reference": "yummer-48213", "created_at": "2026-10-27T02:01:31Z" } ``` ## Tus Inspecciones 360 Cuando Conduce inspecciona un vehículo tuyo, el resultado completo te llega por la API: los 68 puntos, los daños con foto, el diagnóstico OBD2, la valuación de ese día y el informe en PDF. ### Danos la referencia del conductor Al agendar la inspección, la misma `reference` de tus valuaciones. El inspector la anota en la 360. ### Recibe el aviso Cuando la inspección queda lista, a tu servidor le llega `inspection.completed` con su id. ### Pide el resultado `GET /v1/inspections/{id}` trae la inspección completa, y `/report`, el PDF con el certificado. ```json { "id": "insp_4Rk9mT2xQ7vB3nHs", "object": "inspection", "status": "completed", "reference": "yummer-48213", "vehicle": { "vin": "JTDBR32E000000000", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000 }, "score": { "technical": 86, "prequalification": 27, "prequalified": true }, "rating": "good_condition", "items": [ { "key": "first_owner", "label": "¿Es el primer propietario? (N° de propietarios)", "block": "prequalification", "group": "documentos", "result": "no", "owners": 2, "weight": 5, "note": null }, { "key": "compresion_motor", "label": "Condición general de la compresión del motor", "block": "technical", "group": "motor", "result": "pass", "weight": 6, "note": null }, { "key": "tapiceria", "label": "Tapicería interna (asientos, puertas y tableros)", "block": "technical", "group": "interior", "result": "improve", "weight": 1, "note": "Desgaste en el asiento del conductor" } // … y los otros 65 puntos ], "damages": [ { "id": "dmg_1", "view": "left", "description": "Rayón de severidad leve: puerta trasera", "photo_url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_damage_3_20261027_141802_8d2e41c7.webp" } ], "obd2": { "scanned_at": "2026-10-27T14:05:12Z", "codes": [] }, "interview": { "usage": "urban", "collision": false, "stolen_or_recovered": false, "sale_reason": "Cambio de carro", "owner_estimated_price": 25000 }, "test_drive": { "acceleration": true, "no_abnormal_noises": true, "no_vibrations": true, "abs_responds": true, "braking": true, "overall_feel": true }, "observations": { "relevant_info": "Mantenimientos al día en el concesionario.", "technical": "Pastillas de freno delanteras a la mitad." }, "photos": [ { "kind": "section", "section": "photo_vin", "url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_section_0_20261027_141130_5c1f09a2.webp" }, { "kind": "section", "section": "photo_odometer", "url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_section_1_20261027_141131_e7b3d950.webp" }, { "kind": "damage", "section": null, "url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_damage_3_20261027_141802_8d2e41c7.webp" } ], "valuation": { "id": "val_9Tc4wE7yU2iO6pAz", "object": "valuation", "status": "complete", // … el objeto Valuación completo }, "report_url": "https://api.conduceapp.com/v1/inspections/insp_4Rk9mT2xQ7vB3nHs/report", "location": null, "inspected_at": "2026-10-27T14:20:03Z" } ``` ## Cómo calculamos el precio El mismo motor de Precio Real que usa la app de Conduce, paso a paso. ### Buscamos comparables Carros vendidos y publicados en Conduce, y publicaciones de carros en portales de clasificados y redes sociales de Venezuela. Primero de la misma versión; si no alcanzan, de todo el modelo. ### Los llevamos a tu vehículo Cada precio se ajusta al año y al kilometraje de tu vehículo, y los precios pedidos del mercado se llevan a precio de venta. ### Quitamos lo que no cuadra Los precios que se salen de lo normal para ese vehículo no entran al cálculo. En la respuesta los ves con `outlier` en true. ### Tomamos la mediana El precio estimado es la mediana de los comparables ajustados, y el estado de tu vehículo lo mueve hacia arriba o hacia abajo. ## Buenas prácticas Lo que hace que tu integración aguante y no gaste cupo de más. - Guarda el id de cada valuación: la puedes volver a pedir sin gastar cupo. - Manda `reference` con el número de tu conductor o expediente. - Guarda el catálogo un día: marcas, modelos y versiones cambian poco. - Ante un 429 `rate_limited` o un 5xx, espera y reintenta: el 429 trae en `Retry-After` los segundos. Con `monthly_quota_exceeded`, el cupo vuelve el día 1 del mes, en hora de Venezuela. - Llama a la API desde tu servidor, nunca desde una app o una página web. ## ¿Todavía no tienes tu llave? Las llaves las entrega Conduce con el plan de cada cliente. Pídela con el formulario y te contactamos. Si algo falla, mándanos el `X-Request-Id` de la petición. # API de Conduce: referencia (v1) Precio Real e Inspección 360: el valor de mercado de carros y motos en Venezuela, con cada comparable que lo respalda, y el resultado de las inspecciones de tus vehículos. - URL base: https://api.conduceapp.com/v1 - Autenticación: encabezado `Authorization: Bearer ` - Formato: JSON en UTF-8. Fechas en ISO 8601 y UTC. Montos en USD, enteros. ## Introducción La API de Conduce te da dos cosas. Precio Real: el valor de mercado de un carro o una moto en Venezuela, en dólares, con el rango en que se mueve y cada uno de los comparables que lo respaldan. E Inspección 360: el resultado completo de las inspecciones que Conduce hace a tus vehículos, con su informe en PDF. Es una API REST: recibe y devuelve JSON, usa los códigos HTTP de siempre y se autentica con una llave secreta. Toda petición va por HTTPS. El precio sale de una base de más de 15.000 comparables: carros vendidos y publicados en Conduce, y publicaciones de carros en portales de clasificados y redes sociales de Venezuela. Cada precio usa los de su modelo, y cada comparable se lleva al año y al kilometraje de tu vehículo antes de calcular. ### Lo que necesitas - Tu llave secreta, que empieza con `cnd_live_`. - Un servidor que pueda hacer peticiones HTTPS. - Nada más: no hay SDK de Conduce. URL base: `https://api.conduceapp.com/v1` ## Autenticación Cada petición lleva tu llave secreta en el encabezado `Authorization`, como Bearer. Las llaves empiezan con `cnd_live_` y te las entrega Conduce. Tu llave gasta tu cupo: guárdala en tu servidor, en una variable de entorno. Nunca la pongas en una app, en una página web ni en un repositorio. Si falta o no es válida, la respuesta es 401. Si crees que se filtró, pídenos una nueva y que revoquemos la anterior: crear una llave nueva no apaga la anterior; Conduce la revoca cuando se lo pides. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/usage" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ## Errores Los códigos 2xx son éxito; los 4xx, un problema con la petición, y el mensaje dice cuál; los 5xx, una falla de Conduce, que es poco común. | Código | Qué significa | |---|---| | 200 | Todo salió bien. | | 400 | La petición está mal formada: el JSON no se pudo leer. | | 401 | Falta la llave o no es válida. | | 403 | Tu acceso está suspendido. | | 404 | Lo que pides no existe, no es tuyo o el informe todavía no ha llegado. | | 413 | El cuerpo de la petición pasa de 1 MB. | | 422 | Un dato no es válido: `param` dice cuál y `message`, por qué. | | 429 | Pasaste el límite por minuto o el cupo del mes. | | 500, 503 | Falla de Conduce. Reintenta en un momento. | ### Los códigos de error | `code` | HTTP | Cuándo | |---|---|---| | `invalid_json` | 400 | El cuerpo no es un JSON válido. | | `body_too_large` | 413 | El cuerpo de la petición pasa de 1 MB. | | `parameter_missing` | 422 | Falta un dato obligatorio. | | `parameter_invalid` | 422 | Un dato no cumple su regla. | | `brand_not_found` | 422 | La marca no está en el catálogo: `message` trae las más parecidas. | | `resource_missing` | 404 | El id no existe o no es tuyo, o la ruta no existe. | | `report_not_ready` | 404 | El informe en PDF de esa inspección todavía no ha llegado. | | `api_key_missing` | 401 | No mandaste la llave. | | `api_key_invalid` | 401 | La llave no es válida o fue revocada. | | `client_suspended` | 403 | Tu acceso está suspendido. | | `rate_limited` | 429 | Pasaste el límite de peticiones por minuto. | | `monthly_quota_exceeded` | 429 | Llegaste al cupo de valuaciones del mes. | | `api_error` | 500, 503 | Falla de Conduce. | ### El objeto de error - `type` (texto): La familia del error: invalid_request_error, authentication_error, permission_error, rate_limit_error o api_error. - `code` (texto): El error exacto, para tu código: parameter_missing, parameter_invalid, brand_not_found, monthly_quota_exceeded… - `param` (texto o null): El parámetro con el problema, o null si no es de un parámetro. - `message` (texto): Qué pasó, en español, para que lo leas tú. - `doc_url` (texto): La parte de esta documentación que lo explica. ### Ejemplo ```json { "error": { "type": "invalid_request_error", "code": "parameter_missing", "param": "mileage_km", "message": "Falta mileage_km: el kilometraje, en km.", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ## Límites de uso Cada llave tiene un límite de peticiones por minuto: 60, si no acordamos otro. Si te pasas, la respuesta es 429 `rate_limited`: espera los segundos que dice `Retry-After` y reintenta. Las valuaciones no tienen tope mensual, salvo que tu plan diga otra cosa. Si lo tiene, solo cuentan las de `POST /v1/valuations` que dan precio, el mes va del día 1 al último en hora de Venezuela y, al llegar al tope, crear una valuación responde 429 `monthly_quota_exceeded` mientras el resto sigue funcionando. Ese 429 no trae `Retry-After`: el cupo vuelve el día 1. No gastan cupo consultar el catálogo, tu uso o una valuación que ya pediste, ni una valuación sin datos suficientes. Las inspecciones tampoco: ni pedirlas ni la valuación que traen. Cada respuesta te dice cómo vas, menos los 401 y 403: esos no traen los `RateLimit-*`. ### Encabezados de la respuesta - `RateLimit-Limit` (entero): Peticiones por minuto de tu llave. - `RateLimit-Remaining` (entero): Las que te quedan en este minuto. - `RateLimit-Reset` (entero): Segundos para que el minuto se renueve. - `X-Conduce-Quota-Remaining` (entero): Valuaciones que te quedan este mes. Solo viene si tu plan tiene tope; entonces sale en cada respuesta autenticada, menos en un 429 rate_limited. - `Retry-After` (entero): Solo con 429 rate_limited: los segundos que debes esperar antes de reintentar. monthly_quota_exceeded no lo trae: el cupo vuelve el día 1 del mes, en hora de Venezuela. - `X-Request-Id` (texto): El id de la petición. Si nos escribes por un problema, mándanoslo. ### Ejemplo ```http HTTP/1.1 200 OK Content-Type: application/json RateLimit-Limit: 60 RateLimit-Remaining: 58 RateLimit-Reset: 41 X-Conduce-Quota-Remaining: 4688 X-Request-Id: req_9fK2mQ7xL1pR3dZw ``` ## Versiones La versión va en la URL. Hoy es v1. Dentro de v1 solo agregamos: campos nuevos en las respuestas, parámetros opcionales y valores nuevos en las listas. Tu integración debe ignorar los campos que no conoce. Un cambio que rompa algo sale en una versión nueva, y te avisamos antes. La anterior sigue funcionando mientras migras. ## El objeto Valuación Una valuación es el precio de mercado de un vehículo en un momento dado, con todo lo que lo respalda. Se guarda: la puedes volver a pedir con su id y no cambia aunque el mercado se mueva. ### Atributos - `id` (texto): Identificador único. Empieza con val_. - `object` (texto): Siempre valuation. - `status` (texto): complete: hay precio. insufficient_data: todavía no hay vehículos parecidos para dar un precio; esa consulta no gasta cupo. - `vehicle` (objeto): El vehículo valuado, con la marca como quedó en el catálogo. - `type` (texto): car o motorcycle. - `year` (entero): Año del modelo. - `brand` (texto): La marca, con su nombre del catálogo. - `model` (texto): El modelo. - `version` (texto o null): La versión, si la mandaste. - `mileage_km` (entero): Kilómetros recorridos. - `condition` (texto): El estado. - `price` (objeto o null): El precio de mercado en dólares, en enteros. null si status es insufficient_data. - `estimated` (entero): El precio estimado: la mediana de los comparables ya ajustados, movida por el estado del vehículo. - `low` (entero): El piso del rango. - `high` (entero): El techo del rango. - `currency` (texto): Siempre USD. - `confidence` (objeto): Qué tan sólido es el precio. - `level` (texto o null): high: la misma versión y 8 comparables o más en el cálculo. low: 1 o 2 comparables. medium: el resto. null si no hay precio. - `match` (texto o null): version: se comparó con la misma versión. model: con todo el modelo. null si no hay precio. - `comparables_used` (entero): Cuántos comparables entraron al cálculo: los de la lista con outlier en false, aunque la lista se corte en 100. - `comparables` (lista): Cada vehículo que respalda el precio, hasta 100: primero los que entraron al cálculo, del más parecido a tu vehículo en año y kilometraje al menos parecido, y después los que quedaron fuera por salirse de lo normal. - `source` (texto): conduce: un carro de Conduce. market: una publicación de carros en portales de clasificados y redes sociales de Venezuela. - `status` (texto): En Conduce: sold (vendido en Conduce), active (publicado) o inactive (ya no está publicado). En el mercado: listed. - `year` (entero): Año del modelo. - `model` (texto): El modelo del comparable. Si no había suficientes del modelo que pediste (Corolla Cross) y se comparó con el más cercano (Corolla), aquí lo ves. - `version` (texto o null): La versión, si se conoce. - `mileage_km` (entero o null): Kilómetros recorridos. null si la publicación no trae los km o no son creíbles; ese comparable no se ajusta por km. - `price` (entero): El precio del comparable con el que trabaja el cálculo, en dólares. En Conduce, el precio del carro; en el mercado, el precio pedido ya llevado a precio de venta. - `adjusted_price` (entero): price llevado al año y al kilometraje de tu vehículo. Es el que entra al cálculo. - `outlier` (booleano): true si el precio se salía de lo normal para ese vehículo y no entró al cálculo. - `message` (texto o null): Una nota sobre el precio cuando la hay, como «Estimación basada en 2 comparables. Tómala como referencia.». Si no, null. - `reference` (texto o null): Tu referencia, si la mandaste. - `created_at` (fecha): Cuándo se calculó, en ISO 8601 y UTC. ### Ejemplo ```json { "id": "val_7Qm2xK9pT4sW8vLd", "object": "valuation", "status": "complete", "vehicle": { "type": "car", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000, "condition": "good" }, "price": { "estimated": 24589, "low": 22622, "high": 26556, "currency": "USD" }, "confidence": { "level": "high", "match": "version", "comparables_used": 37 }, "comparables": [ { "source": "market", "status": "listed", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 80000, "price": 25081, "adjusted_price": 24581, "outlier": false }, { "source": "conduce", "status": "active", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 73000, "price": 28900, "adjusted_price": 27700, "outlier": false }, { "source": "market", "status": "listed", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 70000, "price": 23179, "adjusted_price": 21679, "outlier": false } // … y los otros 34 comparables ], "message": null, "reference": "yummer-48213", "created_at": "2026-10-27T02:01:31Z" } ``` ## Crear una valuación `POST /v1/valuations` Calcula el precio de mercado de un vehículo con sus datos. Usa los nombres del catálogo para marca, modelo y versión: la marca tiene que estar en el catálogo, y el modelo y la versión se comparan por parecido. confidence.match te dice si se comparó con la misma versión o con todo el modelo, y cada comparable trae su modelo. ### Parámetros - `vehicle_type` (texto, opcional): Carro o moto. Si no lo mandas, es car. Valores: `car` (Carro), `motorcycle` (Moto). - `year` (entero, obligatorio): Año del modelo, de 1950 al año que viene. - `brand` (texto, obligatorio): La marca: su id del catálogo (toyota) o su nombre (Toyota), sin importar mayúsculas ni tildes. Hasta 100 caracteres. Si no está en el catálogo, la respuesta es 422 brand_not_found con las marcas más parecidas. - `model` (texto, obligatorio): El modelo, como sale en el catálogo de esa marca. Hasta 255 caracteres. - `version` (texto, opcional): La versión (SE, XEI, LT…). Con versión el precio es más preciso; sin ella se compara con todo el modelo. Hasta 255 caracteres. - `mileage_km` (entero, obligatorio): Kilómetros recorridos, de 0 a 2.000.000. Si el odómetro marca millas, multiplícalas por 1,609. - `condition` (texto, obligatorio): El estado del vehículo. Valores: `excellent` (Excelente), `very_good` (Muy bueno), `good` (Bueno), `fair` (Regular), `needs_repairs` (Necesita reparación). - `reference` (texto, opcional): Tu referencia para amarrar la valuación a tu sistema, como el número de tu conductor o de su expediente. Hasta 100 caracteres. ### Devuelve El objeto Valuación. Si no hay suficientes vehículos parecidos, status es insufficient_data y price viene en null: esa consulta no gasta cupo. ### Ejemplo ```bash curl https://api.conduceapp.com/v1/valuations \ -H "Authorization: Bearer $CONDUCE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "vehicle_type": "car", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000, "condition": "good", "reference": "yummer-48213" }' ``` ### Respuesta 200: Valuación ```json { "id": "val_7Qm2xK9pT4sW8vLd", "object": "valuation", "status": "complete", "vehicle": { "type": "car", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000, "condition": "good" }, "price": { "estimated": 24589, "low": 22622, "high": 26556, "currency": "USD" }, "confidence": { "level": "high", "match": "version", "comparables_used": 37 }, "comparables": [ { "source": "market", "status": "listed", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 80000, "price": 25081, "adjusted_price": 24581, "outlier": false }, { "source": "conduce", "status": "active", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 73000, "price": 28900, "adjusted_price": 27700, "outlier": false }, { "source": "market", "status": "listed", "year": 2020, "model": "Corolla", "version": "SE", "mileage_km": 70000, "price": 23179, "adjusted_price": 21679, "outlier": false } // … y los otros 34 comparables ], "message": null, "reference": "yummer-48213", "created_at": "2026-10-27T02:01:31Z" } ``` ### Respuesta 200: Sin datos suficientes ```json { "id": "val_2Lp8vN3cR6yB5kTe", "object": "valuation", "status": "insufficient_data", "vehicle": { "type": "car", "year": 1998, "brand": "Fiat", "model": "Tempra", "version": null, "mileage_km": 240000, "condition": "fair" }, "price": null, "confidence": { "level": null, "match": null, "comparables_used": 0 }, "comparables": [], "message": null, "reference": "yummer-50771", "created_at": "2026-10-27T02:03:12Z" } ``` ### Respuesta 413: Cuerpo demasiado grande ```json { "error": { "type": "invalid_request_error", "code": "body_too_large", "param": null, "message": "El cuerpo de la petición pasa de 1 MB.", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ### Respuesta 422: Dato inválido ```json { "error": { "type": "invalid_request_error", "code": "parameter_invalid", "param": "condition", "message": "condition debe ser excellent, very_good, good, fair o needs_repairs.", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ### Respuesta 422: Marca que no existe ```json { "error": { "type": "invalid_request_error", "code": "brand_not_found", "param": "brand", "message": "No encontramos la marca \"Toyta\". ¿Quisiste decir Toyota?", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ### Respuesta 429: Cupo del mes ```json { "error": { "type": "rate_limit_error", "code": "monthly_quota_exceeded", "param": null, "message": "Llegaste al cupo de valuaciones de tu plan para este mes. Se renueva el 1 de noviembre.", "doc_url": "https://docs.conduceapp.com/api#limites" } } ``` ### Respuesta 429: Límite por minuto ```json { "error": { "type": "rate_limit_error", "code": "rate_limited", "param": null, "message": "Pasaste el límite de 60 peticiones por minuto. Espera 41 segundos y vuelve a intentar.", "doc_url": "https://docs.conduceapp.com/api#limites" } } ``` ## Obtener una valuación `GET /v1/valuations/{id}` Devuelve una valuación, la que pediste o la de una de tus Inspecciones 360, igual a como salió ese día, con sus comparables. No gasta cupo. ### Parámetros - `id` (texto, obligatorio): El id de la valuación (val_…), en la ruta. ### Devuelve El objeto Valuación. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/valuations/val_7Qm2xK9pT4sW8vLd" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Valuación ```json { "id": "val_7Qm2xK9pT4sW8vLd", "object": "valuation", "status": "complete", // … el objeto Valuación completo, igual a como salió ese día } ``` ## Listar valuaciones `GET /v1/valuations` Tus valuaciones, de la más nueva a la más vieja, por páginas. Sirve para encontrar las de un conductor con su referencia. No gasta cupo. Incluye las valuaciones que vienen con tus Inspecciones 360 (es el mismo id de inspection.valuation). Esas no gastan cupo. Tu uso del mes está en GET /v1/usage. ### Parámetros - `reference` (texto, opcional): Solo las valuaciones con esa referencia. - `limit` (entero, opcional): Cuántas por página, de 1 a 100. Si no lo mandas, 10. - `starting_after` (texto, opcional): El id de la última que recibiste, para pedir la página siguiente. ### Devuelve Una lista de objetos Valuación. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/valuations?reference=yummer-48213&limit=10" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Lista ```json { "object": "list", "data": [ { "id": "val_7Qm2xK9pT4sW8vLd", "object": "valuation", "status": "complete", "reference": "yummer-48213", // … el objeto Valuación completo } ], "has_more": false } ``` ## El objeto Inspección Una Inspección 360 es la revisión de 68 puntos que hace Conduce a un vehículo: 14 de precalificación (documentos, extras e inspección estática) y 54 técnicos (interior, frontal, motor, bajo chasis, laterales, trasera y exterior). Trae los puntajes, cada punto con su resultado, los daños con su foto, el diagnóstico OBD2 y la valuación del vehículo ese día. Solo ves las inspecciones de tus vehículos. ### Atributos - `id` (texto): Identificador único. Empieza con insp_. - `object` (texto): Siempre inspection. - `status` (texto): completed: la inspección terminó. - `reference` (texto o null): La referencia de tu conductor que se anotó al inspeccionar: la misma de tus valuaciones. - `vehicle` (objeto): El vehículo inspeccionado. - `vin` (texto): El serial (VIN) de 17 caracteres. - `year` (entero o null): Año del modelo. null si la inspección no lo trae. - `brand` (texto o null): La marca. null si la inspección no la trae. - `model` (texto o null): El modelo. null si la inspección no lo trae. - `version` (texto o null): La versión. - `mileage_km` (entero o null): Los kilómetros que marcaba el odómetro. null si la inspección no los trae. - `score` (objeto): Los puntajes de la inspección. - `technical` (entero): Puntaje técnico, de 0 a 100: lo que suman los puntos técnicos que cumplen. - `prequalification` (entero): Puntaje de precalificación, de 0 a 30. - `prequalified` (booleano): true si la precalificación llegó a 25 o más. - `rating` (texto o null): Según el puntaje técnico: excellent (91 a 100), good_condition (80 a 90), needs_attention (1 a 79) o in_progress (puntaje técnico 0). null si la inspección no trae calificación. - `items` (lista): Los 68 puntos del protocolo, cada uno con su resultado. - `key` (texto): El punto: compresion_motor, tapiceria, frenos… - `label` (texto): Su nombre, en español. - `block` (texto): prequalification (14 puntos) o technical (54). - `group` (texto): documentos, extras o estatica en la precalificación; interior, frontal, motor, bajo_chasis, laterales, trasera o exterior en los técnicos. - `result` (texto o null): En los técnicos: pass (cumple), improve (por mejorar) o fail (no cumple). En la precalificación: yes o no. null si ese punto no se contestó (inspección hecha antes de asignártela). - `owners` (entero o null): Solo en first_owner: cuántos dueños ha tenido el vehículo. Su result es yes si fue uno solo. null si ese punto no se contestó (inspección hecha antes de asignártela). - `weight` (entero): Lo que pesa en el puntaje. - `note` (texto o null): Lo que anotó el inspector, si anotó algo. - `damages` (lista): Cada daño marcado en el croquis del vehículo, con su foto. - `id` (texto): Identificador del daño dentro de la inspección: dmg_1, dmg_2… - `view` (texto o null): front, rear, left, right o top: la vista del croquis. null si no se marcó. - `description` (texto): El tipo de daño, su severidad si se marcó y la nota del inspector si anotó algo, como «Rayón de severidad leve: puerta trasera». - `photo_url` (texto o null): La foto del daño. El enlace es público, como en la app: se abre sin tu llave y no vence. null si el daño no tiene foto. - `obd2` (objeto o null): El diagnóstico electrónico OBD2 que se le hizo al vehículo con la inspección. null si no se escaneó. - `scanned_at` (fecha): Cuándo se escaneó. - `codes` (lista): Los códigos de falla. Vacía si no hay fallas. - `code` (texto): El código, como P0420. - `description` (texto): Qué significa. - `severity` (texto): critical, warning o info. - `status` (texto): active: la falla está presente. pending: la computadora la detectó y todavía no la confirma. - `interview` (objeto): Lo que contó el dueño del vehículo en la entrevista. Solo trae lo que se contestó. - `usage` (texto o null): highway (carretera), urban (ciudad) o cargo (carga). null si la respuesta no es una de esas. - `collision` (booleano): true si el vehículo tuvo una colisión. - `collision_parts` (texto o null): Las partes que se afectaron en la colisión. null si el campo quedó vacío. - `stolen_or_recovered` (booleano): true si fue robado o recuperado. - `sale_reason` (texto o null): Por qué lo vende. null si el campo quedó vacío. - `owner_estimated_price` (número o null): El precio que el dueño cree que vale, en dólares. null si no se anotó. - `test_drive` (objeto): La prueba de manejo: cada punto en true si salió bien y en false si falló. Solo trae lo que se probó. - `acceleration` (booleano): Aceleración. - `no_abnormal_noises` (booleano): Sin ruidos anormales. - `no_vibrations` (booleano): Sin vibraciones. - `abs_responds` (booleano): El ABS responde. - `braking` (booleano): Frenado correcto. - `overall_feel` (booleano): Sensación general. - `observations` (objeto): Las observaciones del inspector. Solo trae las que tienen texto. - `relevant_info` (texto): Información relevante. - `technical` (texto): Observación técnica. - `driving` (texto): Observación de manejo. - `photos` (lista): Las fotos de la inspección, incluidas las del VIN y del odómetro. Las firmas no salen: solo van en el PDF. - `kind` (texto): section: una foto de la inspección. damage: la foto de un daño. - `section` (texto o null): A qué parte de la inspección pertenece, como photo_vin (el VIN), photo_odometer (el odómetro) o photo_general. null en las fotos de daños. - `url` (texto): La foto. El enlace es público, como en la app: se abre sin tu llave y no vence. - `valuation` (objeto o null): La valuación del vehículo del día de la inspección o, si Conduce te la asignó después, del día en que te la asignó: el objeto Valuación completo, con sus comparables. Viene incluida y no gasta cupo. null si no se pudo valuar. - `id` (texto): Identificador único. Empieza con val_. - `object` (texto): Siempre valuation. - `status` (texto): complete: hay precio. insufficient_data: todavía no hay vehículos parecidos para dar un precio; esa consulta no gasta cupo. - `vehicle` (objeto): El vehículo valuado, con la marca como quedó en el catálogo. - `type` (texto): car o motorcycle. - `year` (entero): Año del modelo. - `brand` (texto): La marca, con su nombre del catálogo. - `model` (texto): El modelo. - `version` (texto o null): La versión, si la mandaste. - `mileage_km` (entero): Kilómetros recorridos. - `condition` (texto): El estado. - `price` (objeto o null): El precio de mercado en dólares, en enteros. null si status es insufficient_data. - `estimated` (entero): El precio estimado: la mediana de los comparables ya ajustados, movida por el estado del vehículo. - `low` (entero): El piso del rango. - `high` (entero): El techo del rango. - `currency` (texto): Siempre USD. - `confidence` (objeto): Qué tan sólido es el precio. - `level` (texto o null): high: la misma versión y 8 comparables o más en el cálculo. low: 1 o 2 comparables. medium: el resto. null si no hay precio. - `match` (texto o null): version: se comparó con la misma versión. model: con todo el modelo. null si no hay precio. - `comparables_used` (entero): Cuántos comparables entraron al cálculo: los de la lista con outlier en false, aunque la lista se corte en 100. - `comparables` (lista): Cada vehículo que respalda el precio, hasta 100: primero los que entraron al cálculo, del más parecido a tu vehículo en año y kilometraje al menos parecido, y después los que quedaron fuera por salirse de lo normal. - `source` (texto): conduce: un carro de Conduce. market: una publicación de carros en portales de clasificados y redes sociales de Venezuela. - `status` (texto): En Conduce: sold (vendido en Conduce), active (publicado) o inactive (ya no está publicado). En el mercado: listed. - `year` (entero): Año del modelo. - `model` (texto): El modelo del comparable. Si no había suficientes del modelo que pediste (Corolla Cross) y se comparó con el más cercano (Corolla), aquí lo ves. - `version` (texto o null): La versión, si se conoce. - `mileage_km` (entero o null): Kilómetros recorridos. null si la publicación no trae los km o no son creíbles; ese comparable no se ajusta por km. - `price` (entero): El precio del comparable con el que trabaja el cálculo, en dólares. En Conduce, el precio del carro; en el mercado, el precio pedido ya llevado a precio de venta. - `adjusted_price` (entero): price llevado al año y al kilometraje de tu vehículo. Es el que entra al cálculo. - `outlier` (booleano): true si el precio se salía de lo normal para ese vehículo y no entró al cálculo. - `message` (texto o null): Una nota sobre el precio cuando la hay, como «Estimación basada en 2 comparables. Tómala como referencia.». Si no, null. - `reference` (texto o null): Tu referencia, si la mandaste. - `created_at` (fecha): Cuándo se calculó, en ISO 8601 y UTC. - `report_url` (texto o null): El informe en PDF con el certificado: ver Descargar el informe. null mientras el informe no ha llegado. - `location` (texto o null): La ciudad donde se hizo. Por ahora siempre viene en null: el campo queda listo para cuando la tengamos. - `inspected_at` (fecha): Cuándo terminó la inspección, en ISO 8601 y UTC. ### Ejemplo ```json { "id": "insp_4Rk9mT2xQ7vB3nHs", "object": "inspection", "status": "completed", "reference": "yummer-48213", "vehicle": { "vin": "JTDBR32E000000000", "year": 2020, "brand": "Toyota", "model": "Corolla", "version": "SE", "mileage_km": 85000 }, "score": { "technical": 86, "prequalification": 27, "prequalified": true }, "rating": "good_condition", "items": [ { "key": "first_owner", "label": "¿Es el primer propietario? (N° de propietarios)", "block": "prequalification", "group": "documentos", "result": "no", "owners": 2, "weight": 5, "note": null }, { "key": "compresion_motor", "label": "Condición general de la compresión del motor", "block": "technical", "group": "motor", "result": "pass", "weight": 6, "note": null }, { "key": "tapiceria", "label": "Tapicería interna (asientos, puertas y tableros)", "block": "technical", "group": "interior", "result": "improve", "weight": 1, "note": "Desgaste en el asiento del conductor" } // … y los otros 65 puntos ], "damages": [ { "id": "dmg_1", "view": "left", "description": "Rayón de severidad leve: puerta trasera", "photo_url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_damage_3_20261027_141802_8d2e41c7.webp" } ], "obd2": { "scanned_at": "2026-10-27T14:05:12Z", "codes": [] }, "interview": { "usage": "urban", "collision": false, "stolen_or_recovered": false, "sale_reason": "Cambio de carro", "owner_estimated_price": 25000 }, "test_drive": { "acceleration": true, "no_abnormal_noises": true, "no_vibrations": true, "abs_responds": true, "braking": true, "overall_feel": true }, "observations": { "relevant_info": "Mantenimientos al día en el concesionario.", "technical": "Pastillas de freno delanteras a la mitad." }, "photos": [ { "kind": "section", "section": "photo_vin", "url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_section_0_20261027_141130_5c1f09a2.webp" }, { "kind": "section", "section": "photo_odometer", "url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_section_1_20261027_141131_e7b3d950.webp" }, { "kind": "damage", "section": null, "url": "https://conduceapp.com/uploads/verificacion-360/JTDBR32E000000000/v360_damage_3_20261027_141802_8d2e41c7.webp" } ], "valuation": { "id": "val_9Tc4wE7yU2iO6pAz", "object": "valuation", "status": "complete", // … el objeto Valuación completo }, "report_url": "https://api.conduceapp.com/v1/inspections/insp_4Rk9mT2xQ7vB3nHs/report", "location": null, "inspected_at": "2026-10-27T14:20:03Z" } ``` ## Listar inspecciones `GET /v1/inspections` Las inspecciones de tus vehículos, de la más nueva a la más vieja, por páginas. Filtra por la referencia del conductor, por el serial o desde una fecha. No gasta cupo. ### Parámetros - `reference` (texto, opcional): Solo las inspecciones de ese conductor. - `vin` (texto, opcional): Solo las de ese serial (VIN). - `inspected_after` (fecha, opcional): Solo las terminadas después de esa fecha, en ISO 8601. - `limit` (entero, opcional): Cuántas por página, de 1 a 100. Si no lo mandas, 10. - `starting_after` (texto, opcional): El id de la última que recibiste, para pedir la página siguiente. ### Devuelve Una lista de objetos Inspección. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/inspections?reference=yummer-48213&limit=10" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Lista ```json { "object": "list", "data": [ { "id": "insp_4Rk9mT2xQ7vB3nHs", "object": "inspection", "reference": "yummer-48213", "rating": "good_condition", "inspected_at": "2026-10-27T14:20:03Z", // … el objeto Inspección completo } ], "has_more": false } ``` ## Obtener una inspección `GET /v1/inspections/{id}` La inspección completa: los 68 puntos con su resultado, los daños con sus fotos, el OBD2 y la valuación de ese día. No gasta cupo. ### Parámetros - `id` (texto, obligatorio): El id de la inspección (insp_…), en la ruta. ### Devuelve El objeto Inspección. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/inspections/insp_4Rk9mT2xQ7vB3nHs" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Inspección ```json { "id": "insp_4Rk9mT2xQ7vB3nHs", "object": "inspection", "status": "completed", "score": { "technical": 86, "prequalification": 27, "prequalified": true }, "rating": "good_condition", // … el objeto Inspección completo } ``` ## Descargar el informe `GET /v1/inspections/{id}/report` El informe en PDF de la Inspección 360, con el certificado: el mismo que entrega Conduce. Responde application/pdf. Si el informe todavía no llegó, la respuesta es 404 report_not_ready. No gasta cupo. ### Parámetros - `id` (texto, obligatorio): El id de la inspección (insp_…), en la ruta. ### Devuelve El informe con el certificado. ### Ejemplo ```bash curl https://api.conduceapp.com/v1/inspections/insp_4Rk9mT2xQ7vB3nHs/report \ -H "Authorization: Bearer $CONDUCE_API_KEY" \ --fail \ -o informe-360.pdf ``` ### Respuesta 404: Informe sin llegar ```json { "error": { "type": "invalid_request_error", "code": "report_not_ready", "param": null, "message": "El informe PDF de esta inspección todavía no está listo.", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ## Listar marcas `GET /v1/brands` Las marcas que la valuadora conoce, con el id que puedes mandar en brand. Cambian poco: guárdalas un día en tu sistema. No gasta cupo. ### Parámetros - `vehicle_type` (texto, opcional): car o motorcycle. Si no lo mandas, car. Valores: `car` (Carro), `motorcycle` (Moto). ### Devuelve Una lista de marcas. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/brands?vehicle_type=car" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Lista ```json { "object": "list", "data": [ { "id": "chevrolet", "name": "Chevrolet" }, { "id": "ford", "name": "Ford" }, { "id": "toyota", "name": "Toyota" } // … ], "has_more": false } ``` ### Respuesta 422: Tipo de vehículo inválido ```json { "error": { "type": "invalid_request_error", "code": "parameter_invalid", "param": "vehicle_type", "message": "vehicle_type debe ser car o motorcycle.", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ## Listar modelos `GET /v1/models` Los modelos de una marca, del que más publicaciones tiene al que menos. Con year, solo los que tienen publicaciones de ese año, con un año de margen. No gasta cupo. ### Parámetros - `brand` (texto, obligatorio): La marca: su id o su nombre. - `year` (entero, opcional): Solo los modelos con publicaciones de ese año, con un año de margen: con 2020, de 2019 a 2021. - `vehicle_type` (texto, opcional): car o motorcycle. Si no lo mandas, car. Valores: `car` (Carro), `motorcycle` (Moto). ### Devuelve Una lista de modelos. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/models?brand=toyota&year=2020" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Lista ```json { "object": "list", "data": [ { "name": "4Runner" }, { "name": "Corolla" }, { "name": "Fortuner" } // … ], "has_more": false } ``` ### Respuesta 422: Marca que no existe ```json { "error": { "type": "invalid_request_error", "code": "brand_not_found", "param": "brand", "message": "No encontramos la marca \"Toyta\". ¿Quisiste decir Toyota?", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ## Listar versiones `GET /v1/versions` Las versiones de un modelo, hasta 15, con cuántas publicaciones tiene cada una: con más publicaciones, el precio es más preciso. No gasta cupo. ### Parámetros - `brand` (texto, obligatorio): La marca: su id o su nombre. - `model` (texto, obligatorio): El modelo. - `year` (entero, opcional): Solo las versiones con publicaciones de ese año, con un año de margen: con 2020, de 2019 a 2021. - `vehicle_type` (texto, opcional): car o motorcycle. Si no lo mandas, car. Valores: `car` (Carro), `motorcycle` (Moto). ### Devuelve Una lista de versiones. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/versions?brand=toyota&model=Corolla&year=2020" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Lista ```json { "object": "list", "data": [ { "name": "SE", "comparables": 40 }, { "name": "LE", "comparables": 10 }, { "name": "GLI", "comparables": 2 } ], "has_more": false } ``` ### Respuesta 422: Falta un dato ```json { "error": { "type": "invalid_request_error", "code": "parameter_missing", "param": "model", "message": "Falta model: el modelo, como sale en GET /v1/models.", "doc_url": "https://docs.conduceapp.com/api#errores" } } ``` ## Webhooks Cuando pasa algo con una inspección de tus vehículos, te avisamos: mandamos un POST con el evento a la URL de tu servidor. Así no tienes que preguntar a cada rato. La URL y tu secreto de webhooks, que empieza con `whsec_`, los configuramos contigo al darte acceso. La URL tiene que ser `https` y de un servidor público. ### Los eventos | Evento | Cuándo sale | |---|---| | `inspection.completed` | La inspección quedó lista. Sale cuando llega su informe en PDF o, si el informe tarda, unos minutos después de terminada, con `report_url` en null. También sale cuando Conduce te asigna una inspección que ya existía. | | `inspection.report_ready` | Llegó el informe en PDF después de `inspection.completed`, o se reemplazó. | | `inspection.updated` | Conduce corrigió la referencia de la inspección; la de su valuación cambia con ella. | | `inspection.removed` | La inspección ya no es tuya: desde ese momento la API responde 404. Su `data` trae solo `id`, `object` y `reference`. | ### Cómo verificar la firma Cada aviso va firmado en el encabezado `Conduce-Signature`: `t` es la hora del envío y `v1`, la firma HMAC-SHA256 de `t`, un punto y el cuerpo, hecha con tu secreto. Calcúlala con el cuerpo tal como llegó, antes de convertirlo a JSON, y compárala antes de confiar en el aviso. Rechaza también un aviso cuyo `t` difiera más de 5 minutos de tu reloj: cada reintento sale con su `t` nuevo. Para probar tu código: con el secreto `whsec_prueba`, `t` igual a `1793110805` y el cuerpo `{"id":"evt_8Hn3pW5rK2qT"}`, la firma tiene que dar `4bd17dfea4f9b7c1e34d78b32d378a345baca18b6e1eafa9790988a30ae7efee`. El aviso de ejemplo de esta sección también va firmado con `whsec_prueba`, sobre el cuerpo tal como se ve. ### Cómo responder Responde con un 2xx en menos de 10 segundos. Si tu servidor no responde o da un error, reintentamos varias veces, cada vez esperando más. No seguimos redirecciones: un 301 o 302 cuenta como falla. Usa el `id` del evento para no procesar dos veces el mismo aviso. Si tu servidor estuvo caído, ponte al día con `GET /v1/inspections?inspected_after=…`. ### El objeto evento - `id` (texto): Identificador del aviso. Empieza con evt_: úsalo para no procesar dos veces el mismo. - `object` (texto): Siempre event. - `type` (texto): Qué pasó: inspection.completed, inspection.report_ready, inspection.updated o inspection.removed. - `created_at` (fecha): Cuándo pasó, en ISO 8601 y UTC. - `data` (objeto): Un resumen de la inspección: pide el objeto completo con su id. En inspection.removed trae solo id, object y reference. - `id` (texto): El id de la inspección. - `object` (texto): Siempre inspection. - `reference` (texto o null): La referencia de tu conductor. - `rating` (texto o null): La calificación: la misma de rating en el objeto Inspección, que puede venir en null. - `inspected_at` (fecha): Cuándo terminó la inspección. - `report_url` (texto o null): El informe en PDF, o null si todavía no llegó. ### Aviso de ejemplo ```http POST /webhooks/conduce HTTP/1.1 Host: tu-servidor.com Content-Type: application/json User-Agent: Conduce-Webhooks/1 Conduce-Event-Id: evt_8Hn3pW5rK2qT6mXa Conduce-Signature: t=1793110805,v1=5ffe9a04f59022236cf083c595b2b76ce9de504768e5ad324ed4ed8d0f42b7c6 { "id": "evt_8Hn3pW5rK2qT6mXa", "object": "event", "type": "inspection.completed", "created_at": "2026-10-27T14:20:05Z", "data": { "id": "insp_4Rk9mT2xQ7vB3nHs", "object": "inspection", "reference": "yummer-48213", "rating": "good_condition", "inspected_at": "2026-10-27T14:20:03Z", "report_url": "https://api.conduceapp.com/v1/inspections/insp_4Rk9mT2xQ7vB3nHs/report" } } ``` ### La firma en código ```bash t=1793110805 # el t de Conduce-Signature # La firma: t, un punto y el cuerpo crudo, con tu secreto printf '%s.%s' "$t" '{"id":"evt_8Hn3pW5rK2qT"}' \ | openssl dgst -sha256 -hmac whsec_prueba # Con el ejemplo de prueba tiene que dar 4bd17dfea4f9b7c1e34d78b32d378a345baca18b6e1eafa9790988a30ae7efee # Y que t no difiera más de 5 minutos (300 s) de tu reloj: con el t del ejemplo, no pasa d=$(( $(date +%s) - t )); [ "${d#-}" -le 300 ] || echo "t vencido: rechaza el aviso" ``` ```js import crypto from "node:crypto"; // cuerpo: el texto crudo del POST, tal como llegó function avisoValido(cuerpo, firma, secreto, ahora = Date.now() / 1000) { const partes = Object.fromEntries(firma.split(",").map((p) => p.split("="))); // Un aviso de más de 5 minutos no vale: cada reintento sale con su t nuevo const t = Number(partes.t); if (!Number.isFinite(t) || Math.abs(ahora - t) > 300) return false; const esperada = crypto.createHmac("sha256", secreto).update(`${partes.t}.${cuerpo}`).digest("hex"); const a = Buffer.from(esperada); const b = Buffer.from(partes.v1 || ""); return a.length === b.length && crypto.timingSafeEqual(a, b); } // Prueba, con la hora del ejemplo: tiene que dar true avisoValido('{"id":"evt_8Hn3pW5rK2qT"}', "t=1793110805,v1=4bd17dfea4f9b7c1e34d78b32d378a345baca18b6e1eafa9790988a30ae7efee", "whsec_prueba", 1793110805); ``` ```python import hashlib, hmac, time # cuerpo: los bytes crudos del POST, tal como llegaron def aviso_valido(cuerpo: bytes, firma: str, secreto: str, ahora: float | None = None) -> bool: partes = dict(p.split("=", 1) for p in firma.split(",")) # Un aviso de más de 5 minutos no vale: cada reintento sale con su t nuevo ahora = time.time() if ahora is None else ahora t = partes.get("t", "") if not t.isdecimal() or abs(ahora - int(t)) > 300: return False esperada = hmac.new(secreto.encode(), t.encode() + b"." + cuerpo, hashlib.sha256).hexdigest() return hmac.compare_digest(esperada, partes.get("v1", "")) # Prueba, con la hora del ejemplo: tiene que dar True aviso_valido(b'{"id":"evt_8Hn3pW5rK2qT"}', "t=1793110805,v1=4bd17dfea4f9b7c1e34d78b32d378a345baca18b6e1eafa9790988a30ae7efee", "whsec_prueba", 1793110805) ``` ```php // $cuerpo = file_get_contents('php://input'): el cuerpo crudo, tal como llegó function aviso_valido(string $cuerpo, string $firma, string $secreto, ?int $ahora = null): bool { parse_str(str_replace(',', '&', $firma), $partes); // Un aviso de más de 5 minutos no vale: cada reintento sale con su t nuevo $t = $partes['t'] ?? ''; if (!is_string($t) || !ctype_digit($t) || abs(($ahora ?? time()) - (int) $t) > 300) { return false; } $esperada = hash_hmac('sha256', $t . '.' . $cuerpo, $secreto); return hash_equals($esperada, $partes['v1'] ?? ''); } // Prueba, con la hora del ejemplo: tiene que dar true aviso_valido('{"id":"evt_8Hn3pW5rK2qT"}', 't=1793110805,v1=4bd17dfea4f9b7c1e34d78b32d378a345baca18b6e1eafa9790988a30ae7efee', 'whsec_prueba', 1793110805); ``` ## Consultar tu uso `GET /v1/usage` Cuántas valuaciones llevas este mes, cuántas te quedan si tu plan tiene tope y tu límite por minuto. No lleva parámetros y no gasta cupo. ### Devuelve Tu uso del mes. ### Ejemplo ```bash curl "https://api.conduceapp.com/v1/usage" \ -H "Authorization: Bearer $CONDUCE_API_KEY" ``` ### Respuesta 200: Sin tope ```json { "period": { "start": "2026-10-01T04:00:00Z", "end": "2026-11-01T04:00:00Z" }, "valuations": { "used": 312, "limit": null, "remaining": null }, "rate_limit": { "per_minute": 60 } } ``` ### Respuesta 200: Con tope ```json { "period": { "start": "2026-10-01T04:00:00Z", "end": "2026-11-01T04:00:00Z" }, "valuations": { "used": 312, "limit": 5000, "remaining": 4688 }, "rate_limit": { "per_minute": 60 } } ``` ## Herramientas Para integrar más rápido, con o sin un asistente de IA. - **Copiar para LLM**: cada página tiene su botón para copiarla en Markdown y pegarla en ChatGPT, Claude o el asistente de tu editor, o para abrirla directo en ellos. - **llms.txt y llms-full.txt**: [el índice](/llms.txt) de toda la documentación para asistentes de IA, y [todo el texto](/llms-full.txt) en un solo archivo. - **Especificación OpenAPI**: [openapi.json](/openapi.json) describe cada endpoint, parámetro y respuesta. Con ella generas un cliente en tu lenguaje o tus propias pruebas. - **Colección de Postman**: [impórtala](/conduce-api.postman_collection.json) en Postman o Insomnia, pon tu llave en la variable `CONDUCE_API_KEY` y prueba cada endpoint sin escribir código. ## Cambios ### v1 **Primera versión.** Valuaciones con sus comparables, Inspecciones 360 con su informe, catálogo de marcas, modelos y versiones, avisos por webhook y consulta de uso.