{"openapi":"3.0.3","info":{"title":"Quro Integraciones","version":"1.0.0","description":"Webhooks de telemetría en tiempo real (Ruptela, QURO 4G y 2G). Guía completa en /docs."},"components":{"securitySchemes":{"llave":{"type":"http","scheme":"bearer","bearerFormat":"qi_<prefijo>_<secreto>"}},"schemas":{}},"paths":{"/api/v1/campos":{"get":{"summary":"Catálogo de campos de quro.telemetria.v1","tags":["Formato del punto"],"description":"Los nombres de `campos.lista[].campo`. `familias` dice de qué equipos llega cada campo.","security":[],"responses":{"200":{"description":"`{ version, campos: [{ ruta, tipo, descripcion, familias }], ejemplo }`","content":{"application/json":{"schema":{"description":"`{ version, campos: [{ ruta, tipo, descripcion, familias }], ejemplo }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos":{"get":{"summary":"Lista los destinos de la cuenta","tags":["Destinos"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"empresaUid","required":false,"description":"Sólo llaves internas: el cliente por su UID de empresa de QuroGPS (alternativa a cuentaId)."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"query","name":"cuentaId","required":false,"description":"Sólo llaves internas: la cuenta sobre la que se actúa (id interno; o usa empresaUid)."}],"responses":{"200":{"description":"`{ items: [Destino] }` (sin secretos)","content":{"application/json":{"schema":{"description":"`{ items: [Destino] }` (sin secretos)","type":"object","additionalProperties":true}}}}}},"post":{"summary":"Da de alta un destino (y lo prueba)","tags":["Destinos"],"description":"Guarda el destino, cifra sus secretos y, si la configuración general lo pide, lo **prueba** al momento.\nLa respuesta trae `secretoFirma` **una sola vez**: guárdalo para verificar `X-Quro-Firma`. Si lo pierdes, usa `/rotar-firma`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":120,"description":"Nombre del destino (único en la cuenta)."},"url":{"type":"string","pattern":"^https?://","maxLength":2000,"description":"Tu endpoint. Se recomienda https; con http el destino queda marcado como **peligroso**. Nunca IPs privadas o internas."},"metodo":{"type":"string","enum":["POST","PUT","PATCH"],"description":"Método HTTP (por omisión POST)."},"activo":{"type":"boolean","description":"false = no se envía nada (igual que pausar)."},"autenticacion":{"type":"object","additionalProperties":false,"properties":{"tipo":{"type":"string","enum":["ninguna","apikey","basic","bearer","oauth2_cc"],"description":"Cómo se autentica Quro ante tu endpoint. `ninguna` se permite, pero marca el destino como **peligroso**."},"donde":{"type":"string","enum":["header","query"],"description":"apikey: en un header (por omisión) o como parámetro de la URL."},"nombre":{"type":"string","pattern":"^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,100}$","description":"apikey: nombre del header o del parámetro (p. ej. `x-api-key`). Puede ser `authorization` si tu API espera la llave ahí sin «Bearer»."},"valor":{"type":"string","minLength":1,"maxLength":4000,"pattern":"^[^\\r\\n]*$","description":"apikey: la llave · bearer: el token. **Secreto:** se guarda cifrado y nunca se devuelve. Al editar, si no lo mandas, se conserva el anterior."},"usuario":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[^\\r\\n]*$","description":"basic: usuario."},"clave":{"type":"string","minLength":1,"maxLength":1000,"description":"basic: contraseña (secreto)."},"tokenUrl":{"type":"string","pattern":"^https://","maxLength":2000,"description":"oauth2_cc: URL del servidor de tokens (https obligatorio)."},"clienteId":{"type":"string","minLength":1,"maxLength":300,"description":"oauth2_cc: client_id."},"clienteSecreto":{"type":"string","minLength":1,"maxLength":1000,"description":"oauth2_cc: client_secret (secreto)."},"alcance":{"type":"string","maxLength":500,"description":"oauth2_cc: scope (opcional)."}},"required":["tipo"],"description":"Autenticación ante tu endpoint. Se combina con `headers` propios."},"headers":{"type":"array","maxItems":30,"items":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","pattern":"^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,100}$","description":"Nombre del header."},"valor":{"type":"string","maxLength":4000,"pattern":"^[^\\r\\n]*$","description":"Valor. Si `secreto` es true se guarda cifrado; al editar puedes omitirlo para conservarlo."},"secreto":{"type":"boolean","default":false,"description":"true = se cifra y no se vuelve a mostrar."}},"required":["nombre"]},"description":"Headers propios que se agregan a cada petición."},"query":{"type":"array","maxItems":30,"items":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":100},"valor":{"type":"string","maxLength":1000}},"required":["nombre","valor"]},"description":"Parámetros fijos que se agregan a la URL."},"alcance":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["todos","lista"],"description":"`todos` los vehículos de la empresa, o sólo los de `/destinos/{id}/vehiculos`."}},"required":["modo"]},"campos":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["todos","lista"],"description":"`todos`: el punto completo de `quro.telemetria.v1`. `lista`: sólo los campos de `lista`."},"lista":{"type":"array","maxItems":200,"description":"Campos a enviar (ver `GET /api/v1/campos`). `como` cambia el nombre con el que llega (y puede anidar: `gps.lat`).","items":{"type":"object","additionalProperties":false,"properties":{"campo":{"type":"string","enum":["imei","familia","empresa.uid","vehiculo.uid","vehiculo.alias","vehiculo.noEconomico","vehiculo.placa","fechaGps","fechaLocal","recibidoEn","posicion.latitud","posicion.longitud","posicion.velocidad","posicion.rumbo","evento.id","evento.nombre","direccion","chofer.uid","sensores.temperatura","sensores.temperaturaMotor","sensores.rpm","sensores.odometroObd","sensores.distancia","sensores.combustible.tanque1","sensores.combustible.tanque1Porcentaje","sensores.combustible.tanque2","sensores.combustible.tanque2Porcentaje","sensores.combustible.capacidad","sensores.voltajeBateria","sensores.voltajeVehiculo","sensores.motor","sensores.rssi","sensores.anguloSensor","sensores.chapaMagnetica","sensores.domo","sensores.valvula","sensores.fechaUltimoDomo","sensores.fechaUltimaValvula","sensores.codigo"]},"como":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$","maxLength":80}},"required":["campo"]}},"constantes":{"type":"array","maxItems":30,"description":"Valores fijos que se agregan a cada punto (p. ej. `{\"nombre\":\"proveedor\",\"valor\":\"Quro\"}`).","items":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":80},"valor":{"anyOf":[{"type":"string","maxLength":500},{"type":"number"},{"type":"boolean"}],"description":"Texto, número o booleano."}},"required":["nombre","valor"]}}},"required":["modo"]},"formato":{"type":"object","additionalProperties":false,"properties":{"version":{"type":"string","enum":["quro.telemetria.v1"],"description":"Versión del formato. Hoy sólo existe v1; un cambio incompatible será v2 y v1 seguirá funcionando."},"fecha":{"type":"string","enum":["iso_utc","iso_local","epoch_ms"],"description":"`iso_utc` (2026-10-14T21:22:31.000Z), `iso_local` (2026-10-14T15:22:31-06:00, con `zona`) o `epoch_ms` (número)."},"zona":{"type":"string","maxLength":64,"description":"Zona horaria IANA para `iso_local` (por omisión America/Mexico_City)."},"envoltura":{"type":"string","enum":["lote","arreglo","objeto"],"description":"`lote`: `{version, lote, enviadoEn, total, puntos:[…]}` · `arreglo`: `[…]` · `objeto`: un solo punto por petición (requiere entrega `individual`)."}}},"entrega":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["lote","individual"],"description":"`lote`: los puntos que se juntan mientras hay un envío en curso salen juntos. `individual`: una petición por punto."},"maxPorLote":{"type":"integer","minimum":1,"maximum":1000,"description":"Máximo de puntos por petición (lote)."},"esperaMaxMs":{"type":"integer","minimum":0,"maximum":30000,"description":"Lo más que puede esperar un punto para juntarse con otros. Por omisión 0: sale al momento (tiempo real) y sólo se juntan los que llegan mientras tu endpoint responde."},"maxPeticionesPorSeg":{"type":"number","minimum":0.1,"maximum":100,"description":"Límite de peticiones por segundo a tu endpoint."},"timeoutMs":{"type":"integer","minimum":1000,"maximum":30000,"description":"Tiempo máximo de respuesta de tu endpoint."}}}},"required":["nombre","url"],"description":"Un destino nuevo. Sólo `nombre` y `url` son obligatorios; lo demás toma los valores por omisión."},"example":{"nombre":"ERP de logística","url":"https://erp.cliente.com/api/gps","autenticacion":{"tipo":"apikey","donde":"header","nombre":"x-api-key","valor":"<tu-llave>"},"campos":{"modo":"lista","lista":[{"campo":"vehiculo.placa","como":"plate"},{"campo":"posicion.latitud","como":"lat"},{"campo":"posicion.longitud","como":"lng"},{"campo":"fechaGps","como":"timestamp"}]},"formato":{"fecha":"iso_utc","envoltura":"lote"},"entrega":{"modo":"lote","maxPorLote":100}}}},"description":"Un destino nuevo. Sólo `nombre` y `url` son obligatorios; lo demás toma los valores por omisión."},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"empresaUid","required":false,"description":"Sólo llaves internas: el cliente por su UID de empresa de QuroGPS (alternativa a cuentaId)."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"query","name":"cuentaId","required":false,"description":"Sólo llaves internas: la cuenta sobre la que se actúa (id interno; o usa empresaUid)."}],"responses":{"201":{"description":"`{ destino, secretoFirma, prueba, advertencias }`","content":{"application/json":{"schema":{"description":"`{ destino, secretoFirma, prueba, advertencias }`","type":"object","additionalProperties":true}}}},"400":{"description":"Datos inválidos o URL interna","content":{"application/json":{"schema":{"description":"Datos inválidos o URL interna","type":"object","additionalProperties":true}}}},"409":{"description":"Nombre repetido o límite de destinos","content":{"application/json":{"schema":{"description":"Nombre repetido o límite de destinos","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}":{"get":{"summary":"Un destino","tags":["Destinos"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ destino, vehiculos }` (vehiculos = cuántos hay en su lista)","content":{"application/json":{"schema":{"description":"`{ destino, vehiculos }` (vehiculos = cuántos hay en su lista)","type":"object","additionalProperties":true}}}},"404":{"description":"No existe (o no es de tu cuenta)","content":{"application/json":{"schema":{"description":"No existe (o no es de tu cuenta)","type":"object","additionalProperties":true}}}}}},"patch":{"summary":"Cambia un destino","tags":["Destinos"],"description":"Si cambia la conexión (url, método, autenticación, headers o query) se vuelve a probar.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":120,"description":"Nombre del destino (único en la cuenta)."},"url":{"type":"string","pattern":"^https?://","maxLength":2000,"description":"Tu endpoint. Se recomienda https; con http el destino queda marcado como **peligroso**. Nunca IPs privadas o internas."},"metodo":{"type":"string","enum":["POST","PUT","PATCH"],"description":"Método HTTP (por omisión POST)."},"activo":{"type":"boolean","description":"false = no se envía nada (igual que pausar)."},"autenticacion":{"type":"object","additionalProperties":false,"properties":{"tipo":{"type":"string","enum":["ninguna","apikey","basic","bearer","oauth2_cc"],"description":"Cómo se autentica Quro ante tu endpoint. `ninguna` se permite, pero marca el destino como **peligroso**."},"donde":{"type":"string","enum":["header","query"],"description":"apikey: en un header (por omisión) o como parámetro de la URL."},"nombre":{"type":"string","pattern":"^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,100}$","description":"apikey: nombre del header o del parámetro (p. ej. `x-api-key`). Puede ser `authorization` si tu API espera la llave ahí sin «Bearer»."},"valor":{"type":"string","minLength":1,"maxLength":4000,"pattern":"^[^\\r\\n]*$","description":"apikey: la llave · bearer: el token. **Secreto:** se guarda cifrado y nunca se devuelve. Al editar, si no lo mandas, se conserva el anterior."},"usuario":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[^\\r\\n]*$","description":"basic: usuario."},"clave":{"type":"string","minLength":1,"maxLength":1000,"description":"basic: contraseña (secreto)."},"tokenUrl":{"type":"string","pattern":"^https://","maxLength":2000,"description":"oauth2_cc: URL del servidor de tokens (https obligatorio)."},"clienteId":{"type":"string","minLength":1,"maxLength":300,"description":"oauth2_cc: client_id."},"clienteSecreto":{"type":"string","minLength":1,"maxLength":1000,"description":"oauth2_cc: client_secret (secreto)."},"alcance":{"type":"string","maxLength":500,"description":"oauth2_cc: scope (opcional)."}},"required":["tipo"],"description":"Autenticación ante tu endpoint. Se combina con `headers` propios."},"headers":{"type":"array","maxItems":30,"items":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","pattern":"^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,100}$","description":"Nombre del header."},"valor":{"type":"string","maxLength":4000,"pattern":"^[^\\r\\n]*$","description":"Valor. Si `secreto` es true se guarda cifrado; al editar puedes omitirlo para conservarlo."},"secreto":{"type":"boolean","default":false,"description":"true = se cifra y no se vuelve a mostrar."}},"required":["nombre"]},"description":"Headers propios que se agregan a cada petición."},"query":{"type":"array","maxItems":30,"items":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":100},"valor":{"type":"string","maxLength":1000}},"required":["nombre","valor"]},"description":"Parámetros fijos que se agregan a la URL."},"alcance":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["todos","lista"],"description":"`todos` los vehículos de la empresa, o sólo los de `/destinos/{id}/vehiculos`."}},"required":["modo"]},"campos":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["todos","lista"],"description":"`todos`: el punto completo de `quro.telemetria.v1`. `lista`: sólo los campos de `lista`."},"lista":{"type":"array","maxItems":200,"description":"Campos a enviar (ver `GET /api/v1/campos`). `como` cambia el nombre con el que llega (y puede anidar: `gps.lat`).","items":{"type":"object","additionalProperties":false,"properties":{"campo":{"type":"string","enum":["imei","familia","empresa.uid","vehiculo.uid","vehiculo.alias","vehiculo.noEconomico","vehiculo.placa","fechaGps","fechaLocal","recibidoEn","posicion.latitud","posicion.longitud","posicion.velocidad","posicion.rumbo","evento.id","evento.nombre","direccion","chofer.uid","sensores.temperatura","sensores.temperaturaMotor","sensores.rpm","sensores.odometroObd","sensores.distancia","sensores.combustible.tanque1","sensores.combustible.tanque1Porcentaje","sensores.combustible.tanque2","sensores.combustible.tanque2Porcentaje","sensores.combustible.capacidad","sensores.voltajeBateria","sensores.voltajeVehiculo","sensores.motor","sensores.rssi","sensores.anguloSensor","sensores.chapaMagnetica","sensores.domo","sensores.valvula","sensores.fechaUltimoDomo","sensores.fechaUltimaValvula","sensores.codigo"]},"como":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$","maxLength":80}},"required":["campo"]}},"constantes":{"type":"array","maxItems":30,"description":"Valores fijos que se agregan a cada punto (p. ej. `{\"nombre\":\"proveedor\",\"valor\":\"Quro\"}`).","items":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":80},"valor":{"anyOf":[{"type":"string","maxLength":500},{"type":"number"},{"type":"boolean"}],"description":"Texto, número o booleano."}},"required":["nombre","valor"]}}},"required":["modo"]},"formato":{"type":"object","additionalProperties":false,"properties":{"version":{"type":"string","enum":["quro.telemetria.v1"],"description":"Versión del formato. Hoy sólo existe v1; un cambio incompatible será v2 y v1 seguirá funcionando."},"fecha":{"type":"string","enum":["iso_utc","iso_local","epoch_ms"],"description":"`iso_utc` (2026-10-14T21:22:31.000Z), `iso_local` (2026-10-14T15:22:31-06:00, con `zona`) o `epoch_ms` (número)."},"zona":{"type":"string","maxLength":64,"description":"Zona horaria IANA para `iso_local` (por omisión America/Mexico_City)."},"envoltura":{"type":"string","enum":["lote","arreglo","objeto"],"description":"`lote`: `{version, lote, enviadoEn, total, puntos:[…]}` · `arreglo`: `[…]` · `objeto`: un solo punto por petición (requiere entrega `individual`)."}}},"entrega":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["lote","individual"],"description":"`lote`: los puntos que se juntan mientras hay un envío en curso salen juntos. `individual`: una petición por punto."},"maxPorLote":{"type":"integer","minimum":1,"maximum":1000,"description":"Máximo de puntos por petición (lote)."},"esperaMaxMs":{"type":"integer","minimum":0,"maximum":30000,"description":"Lo más que puede esperar un punto para juntarse con otros. Por omisión 0: sale al momento (tiempo real) y sólo se juntan los que llegan mientras tu endpoint responde."},"maxPeticionesPorSeg":{"type":"number","minimum":0.1,"maximum":100,"description":"Límite de peticiones por segundo a tu endpoint."},"timeoutMs":{"type":"integer","minimum":1000,"maximum":30000,"description":"Tiempo máximo de respuesta de tu endpoint."}}},"version":{"type":"integer","minimum":1,"description":"Opcional: la versión que editas. Si alguien lo cambió antes, responde 409 en vez de pisar su cambio."}},"minProperties":1,"description":"Sólo lo que cambia. `formato` y `entrega` se combinan con lo actual; `autenticacion`, `headers`, `query` y `campos` se reemplazan (los secretos no reenviados se conservan)."}}},"description":"Sólo lo que cambia. `formato` y `entrega` se combinan con lo actual; `autenticacion`, `headers`, `query` y `campos` se reemplazan (los secretos no reenviados se conservan)."},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ destino, prueba, advertencias }`","content":{"application/json":{"schema":{"description":"`{ destino, prueba, advertencias }`","type":"object","additionalProperties":true}}}},"409":{"description":"Otro cambio llegó antes (versión) o nombre repetido","content":{"application/json":{"schema":{"description":"Otro cambio llegó antes (versión) o nombre repetido","type":"object","additionalProperties":true}}}}}},"delete":{"summary":"Da de baja un destino","tags":["Destinos"],"description":"Deja de enviar al momento. Su bitácora y reportes se conservan.","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ ok: true }`","content":{"application/json":{"schema":{"description":"`{ ok: true }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/probar":{"post":{"summary":"Prueba el destino ahora","tags":["Destinos"],"description":"Manda una petición real a tu endpoint con su autenticación, headers y firma, **sin puntos** y con `X-Quro-Prueba: 1`. El cuerpo respeta tu envoltura: `lote` → `{…, total: 0, puntos: [], prueba: true}`, `arreglo` → `[]`, `objeto` → `{prueba: true}`.","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ prueba: { ok, http, ms, error, loteId }, destino }`","content":{"application/json":{"schema":{"description":"`{ prueba: { ok, http, ms, error, loteId }, destino }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/vista-previa":{"post":{"summary":"Muestra exactamente lo que recibiría tu endpoint","tags":["Destinos"],"description":"Con puntos de ejemplo, aplica tus campos, nombres, fecha y envoltura. No envía nada. Los secretos salen enmascarados.","parameters":[{"schema":{"type":"string","enum":["ruptela","quro"],"default":"ruptela"},"in":"query","name":"familia","required":false,"description":"Equipos Ruptela traen más sensores que los QURO 4G/2G."},{"schema":{"type":"integer","minimum":1,"maximum":5,"default":2},"in":"query","name":"puntos","required":false},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ peticiones: [{ url, metodo, headers, cuerpo }] }`","content":{"application/json":{"schema":{"description":"`{ peticiones: [{ url, metodo, headers, cuerpo }] }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/pausar":{"post":{"summary":"Pausa el destino","tags":["Destinos"],"description":"Deja de enviar. Lo que llegue mientras está pausado **no** se guarda.","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ destino }`","content":{"application/json":{"schema":{"description":"`{ destino }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/reanudar":{"post":{"summary":"Reanuda el destino","tags":["Destinos"],"description":"Vuelve a enviar y limpia el silencio y los fallos acumulados.","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ destino }`","content":{"application/json":{"schema":{"description":"`{ destino }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/rotar-firma":{"post":{"summary":"Genera un secreto de firma nuevo","tags":["Destinos"],"description":"El anterior deja de usarse **al momento**. El nuevo se muestra una sola vez.","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ secretoFirma, destino }`","content":{"application/json":{"schema":{"description":"`{ secretoFirma, destino }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/vehiculos":{"get":{"summary":"Vehículos del destino","tags":["Vehículos de un destino"],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":5000,"default":1000},"in":"query","name":"limite","required":false},{"schema":{"type":"string","maxLength":36},"in":"query","name":"despuesDe","required":false,"description":"Paginación: el último vehiculoUid de la página anterior."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ total, items: [{ vehiculoUid, imei, alias, agregadoEn }] }`","content":{"application/json":{"schema":{"description":"`{ total, items: [{ vehiculoUid, imei, alias, agregadoEn }] }`","type":"object","additionalProperties":true}}}}}},"put":{"summary":"Reemplaza la lista completa de vehículos","tags":["Vehículos de un destino"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["vehiculos"],"properties":{"vehiculos":{"type":"array","maxItems":10000,"items":{"type":"object","additionalProperties":false,"properties":{"vehiculoUid":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","description":"UUID del vehículo."},"imei":{"type":"string","maxLength":20,"description":"Opcional, sólo informativo."},"alias":{"type":"string","maxLength":200,"description":"Opcional, sólo informativo."}},"required":["vehiculoUid"]}}}}}}},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ total, agregados, quitados }`","content":{"application/json":{"schema":{"description":"`{ total, agregados, quitados }`","type":"object","additionalProperties":true}}}}}},"post":{"summary":"Agrega vehículos (los que ya estaban se conservan)","tags":["Vehículos de un destino"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["vehiculos"],"properties":{"vehiculos":{"type":"array","maxItems":1000,"items":{"type":"object","additionalProperties":false,"properties":{"vehiculoUid":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","description":"UUID del vehículo."},"imei":{"type":"string","maxLength":20,"description":"Opcional, sólo informativo."},"alias":{"type":"string","maxLength":200,"description":"Opcional, sólo informativo."}},"required":["vehiculoUid"]}}}}}}},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ total, agregados }`","content":{"application/json":{"schema":{"description":"`{ total, agregados }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/vehiculos/{vehiculoUid}":{"delete":{"summary":"Quita un vehículo","tags":["Vehículos de un destino"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":36},"in":"path","name":"vehiculoUid","required":true}],"responses":{"200":{"description":"`{ ok, total }`","content":{"application/json":{"schema":{"description":"`{ ok, total }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/receptor":{"get":{"summary":"Lo que llegó al receptor de pruebas de Quro","tags":["Bitácora y reporte"],"description":"Para probar sin tener endpoint propio: da de alta el destino con `url` = `https://integraciones.quro.com.mx/receptor/{id}` (el id lo sabes tras crearlo: créalo con cualquier URL y luego cámbiala). Agrega `?responder=503` para simular fallas. Muestra los últimos 30 envíos: si la firma fue válida, lote, intento, puntos, nombres de headers y el cuerpo (hasta 8 KB). Se guarda sólo en memoria.","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ total, items: [...] }`","content":{"application/json":{"schema":{"description":"`{ total, items: [...] }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/envios":{"get":{"summary":"Bitácora de entregas del destino (30 días)","tags":["Bitácora y reporte"],"description":"Cada intento: fecha, lote, puntos, bytes, intento, HTTP, milisegundos, ok y error. Más reciente primero; para la página siguiente usa `hasta` = `ts` del último.","parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"desde","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"hasta","required":false},{"schema":{"type":"string","enum":["entrega","prueba","reprueba"]},"in":"query","name":"tipo","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"soloFallidos","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"in":"query","name":"limite","required":false},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ items: [...] }`","content":{"application/json":{"schema":{"description":"`{ items: [...] }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/destinos/{id}/reporte":{"get":{"summary":"Reporte diario de entregas del destino","tags":["Bitácora y reporte"],"description":"Por día (UTC): `lotes` peticiones entregadas, `puntos` entregados, `fallidos` intentos fallidos, `reintentos`, `silencios` (veces que se silenció), `descartados` (puntos perdidos por retención vencida o cola llena), `msPromedio`, `msMax` y `tasaExito` = lotes / (lotes + fallidos) en %.","parameters":[{"schema":{"type":"string","format":"date"},"in":"query","name":"desde","required":false,"description":"Día inicial (AAAA-MM-DD, UTC). Por omisión hace 7 días."},{"schema":{"type":"string","format":"date"},"in":"query","name":"hasta","required":false,"description":"Día final (AAAA-MM-DD, UTC). Por omisión hoy."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"query","name":"cuentaId","required":false,"description":"Sólo llaves internas."},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"empresaUid","required":false,"description":"Sólo llaves internas: el cliente por su UID de empresa de QuroGPS (alternativa a cuentaId)."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true,"description":"id del destino"}],"responses":{"200":{"description":"`{ destino, desde, hasta, dias: [...], totales }`","content":{"application/json":{"schema":{"description":"`{ destino, desde, hasta, dias: [...], totales }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/reporte":{"get":{"summary":"Reporte de la cuenta: totales por destino","tags":["Bitácora y reporte"],"parameters":[{"schema":{"type":"string","format":"date"},"in":"query","name":"desde","required":false,"description":"Día inicial (AAAA-MM-DD, UTC). Por omisión hace 7 días."},{"schema":{"type":"string","format":"date"},"in":"query","name":"hasta","required":false,"description":"Día final (AAAA-MM-DD, UTC). Por omisión hoy."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"query","name":"cuentaId","required":false,"description":"Sólo llaves internas."},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"empresaUid","required":false,"description":"Sólo llaves internas: el cliente por su UID de empresa de QuroGPS (alternativa a cuentaId)."}],"responses":{"200":{"description":"`{ desde, hasta, destinos: [...], totales }`","content":{"application/json":{"schema":{"description":"`{ desde, hasta, destinos: [...], totales }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/llaves":{"get":{"summary":"Llaves de la cuenta (sin secretos)","tags":["Llaves del API"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"empresaUid","required":false,"description":"Sólo llaves internas: el cliente por su UID de empresa de QuroGPS (alternativa a cuentaId)."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"query","name":"cuentaId","required":false,"description":"Sólo llaves internas: la cuenta sobre la que se actúa (id interno; o usa empresaUid)."}],"responses":{"200":{"description":"`{ items: [Llave] }`","content":{"application/json":{"schema":{"description":"`{ items: [Llave] }`","type":"object","additionalProperties":true}}}}}},"post":{"summary":"Crea una llave","tags":["Llaves del API"],"description":"La llave completa (`qi_…`) se muestra **una sola vez** en `llave`. Guárdala en un lugar seguro: Quro sólo guarda su hash.\nUna llave de cuenta sólo puede crear llaves de su cuenta y con permisos que ella misma tenga.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":120,"description":"Para qué es la llave (p. ej. «Portal del cliente»)."},"alcances":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["destinos:leer","destinos:escribir","reportes:leer","llaves:gestionar","operador","admin"]},"description":"Permisos. `admin` y `operador` sólo en llaves internas (sin cuenta); `operador` = todo menos llaves, configuración general, límites y suspensión de cuentas. Una llave de cuenta no puede dar más permisos que los suyos."},"ipsPermitidas":{"type":"array","maxItems":50,"items":{"type":"string","maxLength":64},"description":"Opcional: IPs o rangos CIDR desde los que se puede usar."},"expiraEn":{"type":"string","format":"date-time","description":"Opcional: fecha de expiración (ISO 8601)."}},"required":["nombre","alcances"]},"example":{"nombre":"Portal del cliente","alcances":["destinos:leer","destinos:escribir","reportes:leer"]}}}},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"},"in":"query","name":"empresaUid","required":false,"description":"Sólo llaves internas: el cliente por su UID de empresa de QuroGPS (alternativa a cuentaId)."},{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"query","name":"cuentaId","required":false,"description":"Sólo llaves internas: la cuenta sobre la que se actúa (id interno; o usa empresaUid)."}],"responses":{"201":{"description":"`{ llave, datos: Llave }`","content":{"application/json":{"schema":{"description":"`{ llave, datos: Llave }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/llaves/{id}":{"delete":{"summary":"Revoca una llave (de inmediato)","tags":["Llaves del API"],"parameters":[{"schema":{"type":"string","pattern":"^qi_[a-f0-9]{8}$"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"`{ ok: true }`","content":{"application/json":{"schema":{"description":"`{ ok: true }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/yo":{"get":{"summary":"Qué es la llave con la que llamas","tags":["Llaves del API"],"description":"Tipo (`admin`, `operador` o `cuenta`), permisos, expiración y, si es de cuenta, su empresa. Lo usa la consola para saber qué mostrar.","responses":{"200":{"description":"`{ llave: { id, nombre, tipo, alcances, expiraEn }, cuenta }`","content":{"application/json":{"schema":{"description":"`{ llave: { id, nombre, tipo, alcances, expiraEn }, cuenta }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/cuenta":{"get":{"summary":"Mi cuenta: empresa, límites y si los envíos están activos","tags":["Llaves del API"],"responses":{"200":{"description":"`{ cuenta }`","content":{"application/json":{"schema":{"description":"`{ cuenta }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/cuentas":{"get":{"summary":"Lista las cuentas","tags":["Cuentas (llave interna: admin u operador)"],"responses":{"200":{"description":"`{ items: [Cuenta] }`","content":{"application/json":{"schema":{"description":"`{ items: [Cuenta] }`","type":"object","additionalProperties":true}}}}}},"post":{"summary":"Da de alta una empresa","tags":["Cuentas (llave interna: admin u operador)"],"description":"Después crea su llave con `POST /llaves?cuentaId=…`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"empresaUid":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","description":"UUID de la empresa en Quro."},"nombre":{"type":"string","minLength":1,"maxLength":200},"limites":{"type":"object","additionalProperties":false,"properties":{"maxDestinos":{"type":"integer","minimum":0,"maximum":1000},"maxVehiculosPorDestino":{"type":"integer","minimum":0}}}},"required":["empresaUid","nombre"]}}}},"responses":{"201":{"description":"`{ cuenta }`","content":{"application/json":{"schema":{"description":"`{ cuenta }`","type":"object","additionalProperties":true}}}},"409":{"description":"La empresa ya tiene cuenta","content":{"application/json":{"schema":{"description":"La empresa ya tiene cuenta","type":"object","additionalProperties":true}}}}}}},"/api/v1/cuentas/{id}":{"patch":{"summary":"Cambia nombre, estado o límites","tags":["Cuentas (llave interna: admin u operador)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"nombre":{"type":"string","minLength":1,"maxLength":200},"estado":{"type":"string","enum":["activa","suspendida"],"description":"`suspendida` corta al momento sus llaves; sus destinos dejan de recibir."},"limites":{"type":"object","additionalProperties":false,"properties":{"maxDestinos":{"type":"integer","minimum":0,"maximum":1000},"maxVehiculosPorDestino":{"type":"integer","minimum":0}}}},"minProperties":1}}}},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"`{ cuenta }`","content":{"application/json":{"schema":{"description":"`{ cuenta }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/cuentas/{id}/detener-envios":{"post":{"summary":"Deja de enviar a TODOS los destinos del cliente","tags":["Cuentas (llave interna: admin u operador)"],"description":"Al momento: no se encola nada más y se descarta lo pendiente. Sus destinos y llaves se conservan; `reanudar-envios` lo vuelve a encender.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"motivo":{"type":"string","minLength":1,"maxLength":300,"description":"Por qué (queda en la cuenta y en la auditoría)."}},"required":["motivo"]}}}},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"`{ cuenta }`","content":{"application/json":{"schema":{"description":"`{ cuenta }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/cuentas/{id}/reanudar-envios":{"post":{"summary":"Vuelve a enviar a los destinos activos del cliente","tags":["Cuentas (llave interna: admin u operador)"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"`{ cuenta }`","content":{"application/json":{"schema":{"description":"`{ cuenta }`","type":"object","additionalProperties":true}}}}}}},"/api/v1/config":{"get":{"summary":"Configuración general (reintentos, silencio, retención, límites, pruebas)","tags":["Configuración general (sólo admin)"],"responses":{"200":{"description":"`{ config }`","content":{"application/json":{"schema":{"description":"`{ config }`","type":"object","additionalProperties":true}}}}}},"patch":{"summary":"Cambia la configuración general","tags":["Configuración general (sólo admin)"],"description":"Cada sección que mandes se combina con la actual. El despachador la toma en menos de 60 s.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"reintentos":{"type":"object","additionalProperties":false,"properties":{"maximo":{"type":"integer","minimum":0,"maximum":10},"esperasMs":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"integer","minimum":100,"maximum":600000}}}},"silencio":{"type":"object","additionalProperties":false,"properties":{"trasFallosSeguidos":{"type":"integer","minimum":1,"maximum":100},"inicialMin":{"type":"integer","minimum":1,"maximum":1440},"maximoMin":{"type":"integer","minimum":1,"maximum":1440},"factor":{"type":"number","minimum":1,"maximum":10},"repruebaCadaMin":{"type":"integer","minimum":1,"maximum":60}}},"retencion":{"type":"object","additionalProperties":false,"properties":{"silenciadoMin":{"type":"integer","minimum":60,"maximum":1440},"bitacoraDias":{"type":"integer","minimum":1,"maximum":365},"maxPuntosPorDestino":{"type":"integer","minimum":1000,"maximum":5000000}}},"limites":{"type":"object","additionalProperties":false,"properties":{"maxPorLote":{"type":"integer","minimum":1,"maximum":1000},"maxBytesPorPeticion":{"type":"integer","minimum":1024,"maximum":10485760},"timeoutMaxMs":{"type":"integer","minimum":1000,"maximum":60000},"maxDestinosPorCuenta":{"type":"integer","minimum":1,"maximum":1000}}},"pruebas":{"type":"object","additionalProperties":false,"properties":{"alCrear":{"type":"boolean"},"alCambiar":{"type":"boolean"}}},"despacho":{"type":"object","additionalProperties":false,"properties":{"modo":{"type":"string","enum":["apagado","registrar","enviar"],"description":"`apagado`: no se consume ni se envía nada · `registrar`: se arma todo y se cuenta, sin enviar · `enviar`: producción."}},"required":["modo"]}},"minProperties":1}}}},"responses":{"200":{"description":"`{ config }`","content":{"application/json":{"schema":{"description":"`{ config }`","type":"object","additionalProperties":true}}}}}}}},"servers":[{"url":"https://integraciones.quro.com.mx"}],"security":[{"llave":[]}]}