# Tracker International: portal de desarrolladores (texto completo)
Fuente: https://trackerintl.com/devs. Incluye solo las páginas indexables, en Markdown.
---
> Esta página en HTML: https://trackerintl.com/devs
> Estado: Parcial, revisada el 2026-10-05. Cada operación se contrastó con la plataforma en producción por lectura del código y diff, y las respuestas se midieron con datos ficticios en un entorno de pruebas. No se ha probado con una cuenta real de producción.
# Developers
Documentación de la API de lectura de la plataforma GPS de Tracker International: autenticación, vehículos, historial, eventos y geocercas.
Este portal documenta la API de **lectura** de la plataforma GPS de Tracker International: consulta vehículos, posiciones, historial de recorridos, eventos, alertas, geocercas y reportes desde tu propio software. Solo publicamos lo que verificamos, y cada página te dice qué se verificó y cómo.
## Lo esencial
| Dato | Valor |
|---|---|
| Dominio base | `https://plataforma.trackerintl.com`. Si tu cuenta es de un revendedor marca blanca, usa el dominio que te indique quien la administra. |
| Prefijo de las rutas | `/api` |
| Formato | JSON |
| Credencial | `user_api_hash`, en la cabecera `user-api-hash` |
| Referencia máquina-legible | [OpenAPI 3.1](/devs/openapi.json) con solo las operaciones verificadas |
## Autenticación en tres pasos
1. Inicia sesión con `POST /api/login`, enviando tu correo y tu contraseña. La respuesta trae tu `user_api_hash`.
2. Guarda el hash como un secreto, igual que una contraseña: da los mismos permisos que ella.
3. Envíalo en la cabecera `user-api-hash` de cada petición. Por ejemplo: `GET /api/get_devices` lista tus vehículos.
Los detalles, con ejemplos en cURL, JavaScript, Python y PHP, están en [Autenticación](/devs/autenticacion) y en [Primeros pasos](/devs/primeros-pasos).
## Por dónde empezar
- [Primeros pasos](https://trackerintl.com/devs/primeros-pasos): De cero a tu primera consulta: inicia sesión, lista tus vehículos, pide su última posición y trae el historial de un día, con ejemplos ejecutables.
- [Autenticación](https://trackerintl.com/devs/autenticacion): Cómo obtener tu user_api_hash, cómo enviarlo en cada petición y qué permisos concede, con ejemplos en cURL, JavaScript, Python y PHP.
- [Referencia de la API](https://trackerintl.com/devs/referencia): Operaciones de lectura verificadas de la API de la plataforma GPS, agrupadas por tema, con parámetros, respuestas y ejemplos en cuatro lenguajes.
- [Guías](https://trackerintl.com/devs/guias): Guías de integración con lo que la API de lectura permite hoy: sincronizar con un ERP o CRM, seguir posiciones en vivo y consultar historial por periodos.
- [Servidor MCP](https://trackerintl.com/devs/mcp): Servidor MCP de solo lectura para que un asistente de IA consulte tu cuenta de la plataforma GPS: herramientas, instalación, seguridad y límites.
- [Usar esta documentación con IA](https://trackerintl.com/devs/ia): Cómo leer y citar la documentación de la API con asistentes de IA: Markdown de cada página, llms.txt, texto completo y OpenAPI.
## Qué cubre hoy
- **Vehículos y última posición**: listado, filtros y modo delta para sondeo.
- **Historial**: recorrido por tramos de conducción y parada, y posiciones GPS crudas.
- **Eventos y alertas**: reglas de alerta, eventos disparados y conteo por tipo.
- **Geocercas y rutas planificadas**: lectura, y consulta de qué geocercas contienen un punto o un vehículo.
- **Reportes**: tipos disponibles y plantillas guardadas.
- **Cuenta**: datos y permisos del usuario, conductores e historial de comandos enviados.
## Qué no está documentado todavía
- **Operaciones de escritura**: crear o modificar vehículos, geocercas, alertas u otros datos.
- **Webhooks**: no hay documentación verificada. Consulta la [página de webhooks](/devs/referencia/webhooks).
- **SDK, colección Postman, entorno de pruebas y cuenta demo** para la API.
- **Límites de uso oficiales**: solo publicamos los que se pudieron verificar; mira [Límites](/devs/limites).
- **Referencia de sensores configurados y de servicios de mantenimiento programados**: pendiente. Los valores actuales de sensores y servicios de cada vehículo sí vienen dentro de la respuesta de la lista de vehículos.
Si necesitas algo que no está aquí, [escríbenos](/contacto) y te confirmamos el estado real.
## Cómo se verifica esta documentación
Cada página muestra su estado: **Verificado**, **Parcial** o **Pendiente**, la fecha de revisión y cómo se revisó. La matriz completa está en [Estado de la documentación](/devs/estado-de-la-documentacion). Las reglas editoriales del sitio están en la [política editorial](/politica-editorial).
## Acceso
Necesitas un usuario de la plataforma para obtener tu `user_api_hash`. Si aún no tienes uno, [contáctanos](/contacto). Si encuentras un error en esta documentación, escribe a [info@trackerintl.com](mailto:info@trackerintl.com).
---
> Esta página en HTML: https://trackerintl.com/devs/primeros-pasos
> Estado: Parcial, revisada el 2026-10-05. Los endpoints se verificaron en producción por lectura del código y diff. Los ejemplos se ejecutaron contra un servidor simulado con datos ficticios; todavía no se han ejecutado contra una cuenta real.
# Primeros pasos
De cero a tu primera consulta: inicia sesión, lista tus vehículos, pide su última posición y trae el historial de un día, con ejemplos ejecutables.
En esta guía haces cuatro llamadas: iniciar sesión, listar vehículos, pedir sus últimas posiciones y traer el historial de un día. Necesitas un usuario de la plataforma y, según el lenguaje, cURL, Node.js 22, Python 3 con `requests` o PHP 8 con cURL.
> **Valores de ejemplo.**
>
> `TU_API_HASH`, `demo@example.com` y `TU_CONTRASENA` son marcadores. Sustitúyelos por tus valores. Los datos de las respuestas son ficticios. Nunca pegues tu hash real en tickets, repositorios ni URLs compartidas.
## 1. Inicia sesión y obtén tu hash
**Iniciar sesión y obtener el hash**
cURL:
```bash
curl -sS -X POST "https://plataforma.trackerintl.com/api/login" \
-H "Content-Type: application/json" \
-d '{"email":"demo@example.com","password":"TU_CONTRASENA"}'
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const respuesta = await fetch(`${BASE_URL}/api/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "demo@example.com", password: "TU_CONTRASENA" }),
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
respuesta = requests.post(
f"{BASE_URL}/api/login",
json={"email": "demo@example.com", "password": "TU_CONTRASENA"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(["email" => "demo@example.com", "password" => "TU_CONTRASENA"]),
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
La respuesta trae tu `user_api_hash` y tus permisos:
Fragmento del ejemplo de respuesta (datos ficticios; se omiten campos).
```json
{
"status": 1,
"user_api_hash": "TU_API_HASH"
}
```
Copia el valor de `user_api_hash`: lo usarás en las llamadas siguientes. Ver más en [Autenticación](/devs/autenticacion).
## 2. Lista tus vehículos
**Listar vehículos**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_devices" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "limit=50" \
--data-urlencode "page=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
limit: "50",
page: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_devices?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_devices",
headers={"user-api-hash": API_HASH},
params={"limit": "50", "page": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"50", "page" => "1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
La respuesta es un arreglo de grupos; cada grupo trae sus vehículos en `items`. El grupo `0` reúne los vehículos sin grupo. Un vehículo del ejemplo, con algunos de sus campos:
Fragmento del ejemplo de respuesta (datos ficticios; se omiten campos).
```json
{
"id": 9001,
"name": "Camioneta de prueba 01",
"online": "online",
"lat": -17.7733,
"lng": -63.192099999999996,
"speed": 42,
"time": "05-10-2026 16:11:06"
}
```
El campo `id` identifica al vehículo en el resto de la API (por ejemplo, como `device_id`).
## 3. Pide las últimas posiciones
**Posiciones actualizadas desde un instante**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_devices_latest" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "time=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
time: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_devices_latest?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_devices_latest",
headers={"user-api-hash": API_HASH},
params={"time": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
Con `time=1` obtienes todos los vehículos. En consultas posteriores envía el valor `time` que devolvió la respuesta anterior y recibirás solo los vehículos con posición nueva. La guía [Posición en vivo](/devs/guias/posicion-en-vivo) lo explica paso a paso.
## 4. Trae el historial de un día
Sustituye `device_id` por el `id` de uno de tus vehículos. Las fechas se interpretan en la zona horaria de tu cuenta.
**Recorrido de un vehículo por tramos**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_history" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "device_id=9001" \
--data-urlencode "from_date=2026-10-01" \
--data-urlencode "from_time=00:00" \
--data-urlencode "to_date=2026-10-02" \
--data-urlencode "to_time=23:59"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
device_id: "9001",
from_date: "2026-10-01",
from_time: "00:00",
to_date: "2026-10-02",
to_time: "23:59",
});
const respuesta = await fetch(`${BASE_URL}/api/get_history?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_history",
headers={"user-api-hash": API_HASH},
params={"device_id": "9001", "from_date": "2026-10-01", "from_time": "00:00", "to_date": "2026-10-02", "to_time": "23:59"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"9001", "from_date" => "2026-10-01", "from_time" => "00:00", "to_date" => "2026-10-02", "to_time" => "23:59"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
La respuesta trae el recorrido dividido en tramos de conducción y de parada, y totales del periodo. El rango máximo es de 31 días. Un fragmento de los totales:
Fragmento del ejemplo de respuesta (datos ficticios; se omiten campos).
```json
{
"distance_sum": "9.2 Km",
"top_speed": "31 kph",
"move_duration": "1h 28min 0s",
"stop_duration": "30min 0s"
}
```
## Todo junto en un script
Este script encadena los cuatro pasos: inicia sesión, toma el primer vehículo de tu lista y pide su historial.
**Script completo**
cURL:
```bash
#!/usr/bin/env bash
# Requiere curl y jq.
BASE_URL="https://plataforma.trackerintl.com"
# 1. Inicio de sesión
HASH=$(curl -sS -X POST "$BASE_URL/api/login" \
-H "Content-Type: application/json" \
-d '{"email":"demo@example.com","password":"TU_CONTRASENA"}' | jq -r '.user_api_hash')
# 2. Vehículos (arreglo de grupos con sus vehículos en "items")
VEHICULOS=$(curl -sS -G "$BASE_URL/api/get_devices" -H "user-api-hash: $HASH" --data-urlencode "limit=50")
echo "Vehículos: $(echo "$VEHICULOS" | jq '[.[].items[]] | length')"
# 3. Historial del primer vehículo
ID=$(echo "$VEHICULOS" | jq -r '[.[].items[]][0].id')
curl -sS -G "$BASE_URL/api/get_history" -H "user-api-hash: $HASH" \
--data-urlencode "device_id=$ID" \
--data-urlencode "from_date=2026-10-01" --data-urlencode "from_time=00:00" \
--data-urlencode "to_date=2026-10-02" --data-urlencode "to_time=23:59" \
| jq -r '"\(.distance_sum) en \(.items | length) tramos"'
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
async function llamar(ruta, { metodo = "GET", query = {}, cuerpo, hash } = {}) {
const url = `${BASE_URL}${ruta}?${new URLSearchParams(query)}`;
const headers = {};
if (hash) headers["user-api-hash"] = hash;
if (cuerpo) headers["Content-Type"] = "application/json";
const respuesta = await fetch(url, { method: metodo, headers, body: cuerpo && JSON.stringify(cuerpo) });
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status} en ${ruta}`);
return respuesta.json();
}
// 1. Inicio de sesión
const sesion = await llamar("/api/login", {
metodo: "POST",
cuerpo: { email: "demo@example.com", password: "TU_CONTRASENA" },
});
const hash = sesion.user_api_hash;
// 2. Vehículos (arreglo de grupos con sus vehículos en "items")
const grupos = await llamar("/api/get_devices", { hash, query: { limit: "50" } });
const vehiculos = grupos.flatMap((grupo) => grupo.items);
console.log(`Vehículos: ${vehiculos.length}`);
// 3. Historial del primer vehículo
const primero = vehiculos[0];
const historial = await llamar("/api/get_history", {
hash,
query: { device_id: String(primero.id), from_date: "2026-10-01", from_time: "00:00", to_date: "2026-10-02", to_time: "23:59" },
});
console.log(`${primero.name}: ${historial.distance_sum} en ${historial.items.length} tramos`);
```
Python:
```python
import requests
BASE_URL = "https://plataforma.trackerintl.com"
def llamar(ruta, metodo="GET", query=None, cuerpo=None, hash_api=None):
cabeceras = {"user-api-hash": hash_api} if hash_api else {}
respuesta = requests.request(metodo, f"{BASE_URL}{ruta}", params=query, json=cuerpo, headers=cabeceras, timeout=30)
respuesta.raise_for_status()
return respuesta.json()
# 1. Inicio de sesión
sesion = llamar("/api/login", metodo="POST", cuerpo={"email": "demo@example.com", "password": "TU_CONTRASENA"})
hash_api = sesion["user_api_hash"]
# 2. Vehículos (arreglo de grupos con sus vehículos en "items")
grupos = llamar("/api/get_devices", query={"limit": "50"}, hash_api=hash_api)
vehiculos = [vehiculo for grupo in grupos for vehiculo in grupo["items"]]
print(f"Vehículos: {len(vehiculos)}")
# 3. Historial del primer vehículo
primero = vehiculos[0]
historial = llamar(
"/api/get_history",
query={"device_id": str(primero["id"]), "from_date": "2026-10-01", "from_time": "00:00", "to_date": "2026-10-02", "to_time": "23:59"},
hash_api=hash_api,
)
print(f"{primero['name']}: {historial['distance_sum']} en {len(historial['items'])} tramos")
```
PHP:
```php
true, CURLOPT_TIMEOUT => 30];
if ($cuerpo !== null) {
$cabeceras[] = "Content-Type: application/json";
$opciones[CURLOPT_POST] = true;
$opciones[CURLOPT_POSTFIELDS] = json_encode($cuerpo);
}
$opciones[CURLOPT_HTTPHEADER] = $cabeceras;
curl_setopt_array($ch, $opciones);
$texto = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($texto === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo en $ruta\n");
exit(1);
}
return json_decode($texto, true);
}
// 1. Inicio de sesión
$sesion = llamar("/api/login", [], ["email" => "demo@example.com", "password" => "TU_CONTRASENA"]);
$hash = $sesion["user_api_hash"];
// 2. Vehículos (arreglo de grupos con sus vehículos en "items")
$grupos = llamar("/api/get_devices", ["limit" => "50"], null, $hash);
$vehiculos = array_merge(...array_column($grupos, "items"));
echo "Vehículos: " . count($vehiculos) . "\n";
// 3. Historial del primer vehículo
$primero = $vehiculos[0];
$historial = llamar("/api/get_history", [
"device_id" => (string) $primero["id"],
"from_date" => "2026-10-01", "from_time" => "00:00",
"to_date" => "2026-10-02", "to_time" => "23:59",
], null, $hash);
echo $primero["name"] . ": " . $historial["distance_sum"] . " en " . count($historial["items"]) . " tramos\n";
```
## Siguientes pasos
- [Convenciones](/devs/convenciones): zonas horarias, formatos y paginación.
- [Errores](/devs/errores): qué significa cada respuesta de error.
- [Referencia de la API](/devs/referencia): todas las operaciones documentadas.
- [Guías](/devs/guias): sincronizar con un ERP o CRM, posición en vivo e historial por periodos.
---
> Esta página en HTML: https://trackerintl.com/devs/autenticacion
> Estado: Parcial, revisada el 2026-10-05. Ruta y código confirmados en producción por diff. Los errores 401 y 422 se midieron en un entorno de pruebas; la respuesta 200 del inicio de sesión se lee del código y la caducidad del hash no se midió en producción.
# Autenticación
Cómo obtener tu user_api_hash, cómo enviarlo en cada petición y qué permisos concede, con ejemplos en cURL, JavaScript, Python y PHP.
La API usa un único tipo de credencial: el **`user_api_hash`** de un usuario de la plataforma. Lo obtienes una vez con tu correo y tu contraseña y lo envías en cada petición.
## Cómo funciona
1. `POST /api/login` con `email` y `password` devuelve el `user_api_hash`.
2. En cada llamada siguiente envías ese valor en la cabecera `user-api-hash`.
3. La plataforma resuelve a qué usuario pertenece el hash y aplica sus permisos.
Si tu plataforma lo tiene habilitado, el hash también puede consultarse en la ficha del cliente dentro del panel de administración.
## Enviar la credencial
| Forma | Cómo | Recomendación |
|---|---|---|
| Cabecera | `user-api-hash: TU_API_HASH` | Recomendada |
| Parámetro | `?user_api_hash=TU_API_HASH` (query o cuerpo) | Evítala: las URL quedan en registros de acceso |
`Authorization: Bearer` **no funciona** en esta API.
**Datos y permisos del usuario**
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_user_data" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_user_data`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_user_data",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
## Caducidad del hash
La caducidad **no está medida en producción**. Según la lectura del código, el hash no caduca por tiempo y se regenera cuando el usuario cambia su contraseña; al regenerarse, el valor anterior deja de servir. Hasta tener una medición real, trátalo como un dato por confirmar y prepara tu integración para volver a iniciar sesión si recibes un `401`.
## Qué permisos tiene el hash
El hash concede **los mismos permisos que la contraseña del usuario**: no existen claves de solo lectura, claves por cliente ni alcances por módulo. Los permisos del usuario (ver, editar y eliminar por módulo) se aplican en cada petición, y `GET /api/get_user_data` te devuelve la matriz completa. Por eso te recomendamos:
- Crear un usuario dedicado a la integración, con permisos únicamente de "ver".
- Guardar el hash en un gestor de secretos o en variables de entorno, nunca en el código ni en el navegador de tus usuarios.
- No enviarlo en la URL.
## Errores de autenticación
| Respuesta | Causa probable | Qué hacer |
|---|---|---|
| `401` con `{"status": 0, "message": "..."}` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor | Verifica el hash; si cambió la contraseña, vuelve a iniciar sesión; si todo está bien, contacta a quien administra tu cuenta |
| `422` en el inicio de sesión | Faltan `email` o `password` | Envía ambos campos |
Consulta el detalle en [Errores](/devs/errores) y los límites de intentos en [Límites](/devs/limites).
## Operación de inicio de sesión
## Obtener el hash de API (user_api_hash)
**POST** `/api/login` · Estado: Parcial
Canjea correo y contraseña por el `user_api_hash`, la credencial que se envía en el resto de las peticiones. Acepta cuerpo JSON o formulario. Si el usuario aún no tiene hash, la plataforma lo genera en ese momento.
Autenticación. Sin autenticación. Límite de la plataforma: 60 peticiones por minuto.
- Según el código, el hash no caduca por tiempo y cambia cuando el usuario cambia su contraseña. No se midió en producción.
- Límite de 60 peticiones por minuto (valor por defecto del código; el valor de producción no está confirmado).
- El hash concede los mismos permisos que la contraseña del usuario. No existen claves de solo lectura ni por alcance.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `email` | cuerpo | string | Sí | |
| `password` | cuerpo | string | Sí | |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -X POST "https://plataforma.trackerintl.com/api/login" \
-H "Content-Type: application/json" \
-d '{"email":"demo@example.com","password":"TU_CONTRASENA"}'
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const respuesta = await fetch(`${BASE_URL}/api/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "demo@example.com", password: "TU_CONTRASENA" }),
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
respuesta = requests.post(
f"{BASE_URL}/api/login",
json={"email": "demo@example.com", "password": "TU_CONTRASENA"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(["email" => "demo@example.com", "password" => "TU_CONTRASENA"]),
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `422` | Faltan email/password. Forma {status:0,errors:{campo:[..]}} (sin statusCode). |
| `429` | Límite de peticiones superado (inferido del comportamiento estándar; no medido). |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `user_api_hash` | string | Credencial de la API. Larga vida: solo cambia si se cambia la contraseña del usuario. |
| `permissions` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"user_api_hash": "TU_API_HASH",
"permissions": {
"devices": {
"view": true,
"edit": false,
"remove": false
},
"alerts": {
"view": true,
"edit": false,
"remove": false
},
"events": {
"view": true,
"edit": false,
"remove": false
},
"geofences": {
"view": true,
"edit": false,
"remove": false
},
"routes": {
"view": true,
"edit": false,
"remove": false
},
"reports": {
"view": true,
"edit": false,
"remove": false
},
"history": {
"view": true,
"edit": false,
"remove": false
},
"send_command": {
"view": false,
"edit": false,
"remove": false
}
}
}
```
### Verificación
Estado: Parcial. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y código idéntico al verificado. Los errores 401 y 422 se midieron en un entorno de pruebas; la respuesta 200 se lee del código porque no se hizo un inicio de sesión real.
---
> Esta página en HTML: https://trackerintl.com/devs/convenciones
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las formas de respuesta se midieron con datos ficticios en un entorno de pruebas. No probado con una cuenta real.
# Convenciones
URL base, formatos, zonas horarias, idioma y los cuatro tipos de paginación de la API de la plataforma, tal como se midieron.
Estas reglas valen para todas las operaciones de la [referencia](/devs/referencia).
## URL base y métodos
- Dominio base: `https://plataforma.trackerintl.com`. Si tu cuenta pertenece a un revendedor marca blanca, usa el dominio que te indique quien la administra.
- Todas las rutas empiezan por `/api`.
- Usa `GET` para consultar y `POST` solo para iniciar sesión. Algunas rutas aceptan otros métodos HTTP, pero no se recomienda usarlos.
- Las respuestas son JSON.
## Idioma
Los textos traducibles de la plataforma (por ejemplo, nombres de tipos de alerta o mensajes de error) salen en el idioma indicado con el parámetro `lang` (`es`, `en`) o con la cabecera `Accept-Language`.
## Fechas y zonas horarias
- **Entrada**: las fechas y horas que envías (`from_date`, `from_time`, `date_from`…) se interpretan en la **zona horaria configurada en la cuenta**, no en UTC. Formatos: fecha `AAAA-MM-DD`, hora `HH:MM` o `HH:MM:SS`.
- **Salida**: conviven dos tipos de campos. Los formateados (por ejemplo `time`, `raw_time`) usan la zona y el formato de la cuenta; en las mediciones el formato fue `dd-mm-AAAA HH:mm:ss`. Los campos crudos (`device_time`, `created_at`, `updated_at`…) vienen en UTC.
- Excepciones: `date_from` y `date_to` de `get_alerts_summary` se comparan sin conversión, en UTC, y `time` de `get_devices_latest` son segundos Unix en UTC.
Cada campo indica su zona en la [referencia](/devs/referencia).
## Paginación
La API no tiene un único esquema de paginación. Estos son los que se midieron:
| Operación | Cómo se pagina | Tamaño de página |
|---|---|---|
| `get_devices` | `page` y `limit`; pagina por vehículo antes de agrupar | `limit` sin tope en el código; se recomienda 200 como máximo. Con solo `page`, 100 |
| `get_devices_latest` | `page` y `limit` | 100 por defecto si envías `page` |
| `get_history_messages` | `page` y `limit`; la respuesta trae `current_page`, `last_page` y `total` | `limit` solo admite 50, 100, 200, 300, 400, 500 o 1000; cualquier otro valor se sustituye por 50 |
| `get_events` | Sin `page`: cursor (`next_cursor` en la respuesta, que envías como `cursor`). Con `page`: totales | `limit` 30 por defecto, 1000 como máximo |
| `get_routes` | `page` | 500 fijos |
| `get_reports`, `get_user_drivers` | `page` y `limit` | 10 por defecto |
| `sent_commands` | `page` | 15 fijos |
## Detalles que conviene conocer
- **Cadenas JSON dentro de JSON**: algunos campos llegan como texto que debes decodificar, por ejemplo `coordinates` en las geocercas o `sensors`, `services` y `tail` en `get_devices_latest`.
- **Valores sin dato**: varios campos de texto usan `"-"` cuando no hay valor.
- **Errores con HTTP 200**: algunas validaciones responden `200` con `{"status": 0, "errors": {...}}`. Comprueba siempre `status`. Mira [Errores](/devs/errores).
- **Permisos**: si el usuario no tiene permiso sobre un módulo, algunas operaciones devuelven una lista vacía con `200` y otras un `403`. Cada operación lo indica.
---
> Esta página en HTML: https://trackerintl.com/devs/errores
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las formas de error se midieron en un entorno de pruebas. El 429 es inferido del comportamiento estándar y no se midió.
# Errores
Formas de error de la API de la plataforma, qué significa cada código HTTP y cómo reaccionar, incluidos los errores que llegan con HTTP 200.
La API devuelve errores con tres formas distintas. Conocerlas evita que trates como correcta una respuesta que no lo es.
## Formas de error
**Autenticación (`401`)**:
```json
{
"status": 0,
"message": "Wrong credentials."
}
```
El texto de `message` se traduce según el idioma de la petición.
**Errores estándar (`403`, `404`, `422`, `500`)**: un objeto con `statusCode` (igual al código HTTP), `message`, `status` en `0`, y, cuando aplica, `errors` con un arreglo de mensajes por campo. En los `403` llega además `perm` en `0`.
**Validación del inicio de sesión (`422`)**: sin `statusCode`:
```json
{
"status": 0,
"errors": {
"email": ["The Email field is required."],
"password": ["The Password field is required."]
}
}
```
**Errores con HTTP 200**: algunas validaciones, por ejemplo `point_in_geofences` sin `lat` o `lng`, responden `200` con `{"status": 0, "errors": {...}}`. Revisa siempre `status`, no solo el código HTTP.
## Códigos y cómo reaccionar
| Código | Significado | Qué hacer |
|---|---|---|
| `200` con `status` en `0` | Validación fallida con HTTP 200 | Corrige los parámetros que indica `errors` |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida o API deshabilitada | Verifica el hash y vuelve a iniciar sesión; si persiste, contacta a quien administra tu cuenta |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación | Revisa tus permisos con `get_user_data`; usa solo `id` de vehículos que te devolvió tu propia lista |
| `404` | Recurso inexistente (vehículo, o usuario sin geocercas en las consultas de geocercas) | Verifica el `id` |
| `422` | Faltan parámetros, formato de hora inválido, rango mayor de 31 días en `get_history` o vehículo vencido | Corrige la petición |
| `429` | Límite de peticiones superado (inferido; no medido) | Espera y reintenta con retroceso exponencial. Mira [Límites](/devs/limites) |
| `500` | Error interno; el mensaje es genérico | Reintenta una vez; si persiste, contacta a soporte |
## Un caso conocido de `500`
En un entorno de pruebas, `get_devices` y `get_devices_latest` fallaron con `500` cuando algún vehículo de la cuenta tenía el icono sin asignar. No sabemos cuántos vehículos de producción están en ese caso. Si recibes un `500` constante en esas dos operaciones, avisa a quien administra tu cuenta.
## Buenas prácticas
- Registra el código HTTP, el `message` y la ruta, pero **nunca** el hash.
- Reintenta solo errores `429` y `5xx`, con espera creciente. No reintentes `401`, `403`, `404` ni `422` sin cambiar algo.
- Trata `403` y `404` como datos: pueden significar "este usuario no tiene acceso", no "la API falló".
---
> Esta página en HTML: https://trackerintl.com/devs/limites
> Estado: Parcial, revisada el 2026-10-05. Valores leídos del código de la plataforma y contrastados con producción por diff. No se leyó la configuración de producción, así que cada cifra es el valor por defecto del código, no una medición.
# Límites
Qué límites de uso se conocen de la API (intentos de login, geocercas, rango de historial, tamaño de página) y cuáles no están confirmados.
Esta página solo publica los límites que se pudieron verificar. Los valores de producción pueden diferir de los valores por defecto del código; donde lo sabemos, lo decimos.
## Límites de peticiones
| Operación | Límite | Estado |
|---|---|---|
| `POST /api/login` | 60 peticiones por minuto | Valor por defecto del código; el de producción no está confirmado |
| `GET /api/point_in_geofences` | 30 peticiones por minuto | Medido en un entorno de pruebas |
| `GET /api/devices_in_geofences` | 1800 peticiones por minuto | Leído del código |
| Resto de operaciones | Sin límite global en el código | Un proxy o una capa de red delante de la plataforma podría añadir límites que no se verificaron |
Cuando superes un límite, espera y reintenta con espera creciente. No publicamos todavía un límite global ni una frecuencia de sondeo recomendada.
## Límites de datos
| Qué | Límite | Estado |
|---|---|---|
| Rango de `get_history` | 31 días | Valor por defecto; el de producción no está confirmado. Si lo superas, la API responde `422` |
| Rango de `get_history_messages` | Sin límite en el código | Consulta por tramos de hasta 31 días, como en `get_history` |
| `limit` de `get_history_messages` | 50, 100, 200, 300, 400, 500 o 1000 | Verificado; otro valor se sustituye por 50 sin avisar |
| `limit` de `get_events` | 1000 como máximo; 30 por defecto | Verificado |
| `limit` de `get_devices` | Sin tope en el código | Se recomienda no pasar de 200 |
| Tamaño de página fijo | `get_routes` 500; `sent_commands` 15 | Verificado |
| Tamaño de `get_reports_types` | Unos 128 KB en una cuenta de pruebas | Medido; depende de la cuenta |
## Qué no sabemos todavía
- Cuántos días de historial se conservan.
- Si existe una cuota diaria o mensual por plan.
- Qué límites añade la infraestructura que está delante de la plataforma.
- La frecuencia máxima recomendada de sondeo.
Cuando tengamos mediciones reales de producción, las publicaremos aquí con su fecha.
---
> Esta página en HTML: https://trackerintl.com/devs/referencia
> Estado: Verificado, revisada el 2026-10-05. Generada a partir del OpenAPI público, que se verificó contra producción por lectura del código y diff. Cada operación declara su propio estado.
# Referencia de la API
Operaciones de lectura verificadas de la API de la plataforma GPS, agrupadas por tema, con parámetros, respuestas y ejemplos en cuatro lenguajes.
La referencia se genera desde el [OpenAPI público](/devs/openapi.json), que solo contiene operaciones de lectura verificadas. Antes de empezar, lee [Autenticación](/devs/autenticacion) y [Convenciones](/devs/convenciones).
Todas las operaciones se llaman con `GET`, salvo el inicio de sesión, que usa `POST`, y se autentican con la cabecera `user-api-hash`.
## Grupos
- [Dispositivos](https://trackerintl.com/devs/referencia/dispositivos): Lista tus vehículos con su última posición y estado, y consulta solo los que cambiaron desde un instante. Parámetros, respuestas y ejemplos.
- [Historial y rutas](https://trackerintl.com/devs/referencia/historial-y-rutas): Recorrido de un vehículo por tramos de conducción y parada, y posiciones GPS crudas paginadas. Rango máximo, zona horaria y ejemplos.
- [Eventos y alertas](https://trackerintl.com/devs/referencia/eventos-y-alertas): Reglas de alerta, eventos que dispararon y conteo por tipo. Paginación por cursor o por página, filtros y zona horaria.
- [Geocercas](https://trackerintl.com/devs/referencia/geocercas): Consulta tus geocercas y rutas planificadas, y averigua qué geocercas contienen un punto o la posición actual de un vehículo.
- [Reportes](https://trackerintl.com/devs/referencia/reportes): Catálogo de tipos de reporte de tu cuenta y plantillas guardadas. La generación de reportes no está documentada.
- [Usuario](https://trackerintl.com/devs/referencia/usuario): Datos y permisos del usuario de la API, sus conductores y el historial de comandos enviados a los equipos.
- [Webhooks](https://trackerintl.com/devs/referencia/webhooks): Estado de los webhooks en la API: no hay documentación verificada. Qué hacer mientras tanto para recibir eventos con la API de lectura.
## Qué hay en cada grupo
- **Dispositivos**: `get_devices` y `get_devices_latest`.
- **Historial y rutas**: `get_history` y `get_history_messages`.
- **Eventos y alertas**: `get_events`, `get_alerts` y `get_alerts_summary`.
- **Geocercas**: `get_geofences`, `point_in_geofences`, `devices_in_geofences` y `get_routes`.
- **Reportes**: `get_reports` y `get_reports_types`.
- **Usuario**: `get_user_data`, `get_user_drivers` y `sent_commands`.
- **Autenticación**: `login`, en la página de [Autenticación](/devs/autenticacion).
## Qué no incluye esta referencia
No se documentan operaciones de escritura, de administración de clientes ni de la aplicación móvil de administración. Tampoco hay webhooks documentados: mira [Webhooks](/devs/referencia/webhooks). El estado de cada operación está en [Estado de la documentación](/devs/estado-de-la-documentacion).
---
> Esta página en HTML: https://trackerintl.com/devs/referencia/dispositivos
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las respuestas se midieron con datos ficticios en un entorno de pruebas. No probado con una cuenta real.
# Dispositivos
Lista tus vehículos con su última posición y estado, y consulta solo los que cambiaron desde un instante. Parámetros, respuestas y ejemplos.
En la API, cada **vehículo** es un *dispositivo* (`device`): un equipo GPS asociado a un vehículo de tu cuenta. Su campo `id` es el que usas como `device_id` en el resto de las operaciones.
## Estado del vehículo
El campo `online` resume el estado calculado por la plataforma:
| Valor | Significado |
|---|---|
| `online` | En movimiento |
| `engine` | Motor encendido y detenido |
| `ack` | Detenido |
| `offline` | Sin conexión |
| `blocked` | Bloqueado |
El campo `time` es una etiqueta de la última hora de contacto y puede ser un texto traducido, como "No conectado", en lugar de una fecha. Para la hora de la última posición usa `timestamp` (segundos Unix, UTC) o `last_position_time`.
## Qué lista usar
- `get_devices` para el inventario completo, con filtros por texto, IMEI, placa, grupo o estado.
- `get_devices_latest` para sondeo: devuelve solo los vehículos con posición nueva desde un instante.
En la lista de vehículos, `sensors` y `services` traen los valores actuales de cada sensor y servicio del vehículo; en `get_devices_latest` esos campos llegan como cadenas JSON.
## Listar vehículos agrupados con su última posición
**GET** `/api/get_devices` · Estado: Verificado
Devuelve un arreglo de grupos `{id, title, items[]}`; el grupo `0` reúne los vehículos sin grupo. Cada elemento trae la última posición válida, el estado calculado, los sensores y los servicios. Con `page` o `limit` pagina por vehículo antes de agrupar, así que un grupo puede repartirse entre páginas; si solo envías `page`, `limit` vale 100. Sin ninguno de los dos recorre todos los vehículos. Si el usuario no tiene permiso para ver vehículos responde `[]` con HTTP 200.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
- Medido en un entorno de pruebas: la respuesta falla con HTTP 500 si algún vehículo de la cuenta tiene el icono sin asignar. No se comprobó cuántos vehículos de producción están en ese caso.
- Los filtros de texto (`s`) buscan en nombre, IMEI, placa y otros campos del vehículo.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `s` | query | string | No | Búsqueda de texto; varios términos separados por ";" (OR). Medido con valor simple. |
| `limit` | query | integer | No | Vehículos por página. Sin tope en el código (se recomienda ≤ 200). |
| `page` | query | integer | No | Página (pasar page o limit activa la paginación). |
| `device_id` | query | integer | No | Filtro por ID. |
| `id` | query | integer | No | Equivale a device_id (también filtra). |
| `imei` | query | string | No | IMEI exacto. |
| `plate_number` | query | string | No | Placa exacta. |
| `group_id` | query | integer | No | 0 = sin grupo. |
| `status` | query | string ("expired", "offline", "online", "inactive", "move", "stop", "park", "idle", "engine_on", "engine_off", "blocked", "not_connected", "never_connected") | No | Estado, puede repetirse como arreglo. |
| `online` | query | integer | No | Minutos: conectados en los últimos N minutos. |
| `offline` | query | integer | No | Minutos: sin conexión hace más de N minutos. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_devices" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "limit=50" \
--data-urlencode "page=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
limit: "50",
page: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_devices?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_devices",
headers={"user-api-hash": API_HASH},
params={"limit": "50", "page": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"50", "page" => "1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
Ejemplo de respuesta 200 (datos ficticios):
```json
[
{
"id": 0,
"title": "Sin grupo",
"items": [
{
"id": 9001,
"alarm": 0,
"name": "Camioneta de prueba 01",
"online": "online",
"time": "05-10-2026 16:11:06",
"no_position_update": false,
"last_position_time": "05-10-2026 16:10:58",
"timestamp": 1791231058,
"acktimestamp": 1791231066,
"lat": -17.7733,
"lng": -63.192099999999996,
"course": 208,
"speed": 42,
"altitude": 415,
"icon_type": "rotating",
"icon_color": "green",
"icon_colors": {
"moving": "green",
"stopped": "yellow",
"offline": "red",
"engine": "yellow",
"blocked": "black"
},
"icon": {
"id": 79,
"user_id": null,
"type": "rotating",
"order": null,
"width": 25,
"height": 51,
"path": "images/device_icons/ejemplo_online.png",
"by_status": 1
},
"power": "-",
"address": "-",
"protocol": "gt06",
"driver": "-",
"driver_data": {
"id": null,
"user_id": null,
"device_id": null,
"name": null,
"rfid": null,
"phone": null,
"email": null,
"description": null,
"created_at": null,
"updated_at": null
},
"sensors": [
{
"id": 70001,
"type": "battery",
"name": "Bateria",
"show_in_popup": 1,
"value": "87 %",
"val": 87,
"scale_value": 87,
"tag_name": "batterylevel"
},
{
"id": 70002,
"type": "ignition",
"name": "Ignicion",
"show_in_popup": 1,
"value": "Encendido",
"val": true,
"scale_value": 1,
"tag_name": "ignition"
}
],
"services": [
{
"id": 80001,
"name": "Cambio de aceite",
"value": "Days Left (56d.)",
"expiring": false,
"expiration_by": "days"
}
],
"tail": [
{
"lat": "-17.7811",
"lng": "-63.1843"
},
{
"lat": "-17.7820",
"lng": "-63.1835"
}
],
"distance_unit_hour": "kph",
"unit_of_distance": "Km",
"unit_of_altitude": "m",
"unit_of_capacity": "L",
"stop_duration": "-",
"stop_duration_sec": 0,
"moved_timestamp": 1791231000,
"engine_status": true,
"detect_engine": "gps",
"engine_hours": "gps",
"total_distance": 12176.23,
"inaccuracy": null,
"sim_expiration_date": "-",
"device_data": {
"id": 9001,
"user_id": 1,
"traccar_device_id": 9001,
"icon_id": 79,
"active": 1,
"enable": 1,
"deleted": 0,
"name": "Camioneta de prueba 01",
"imei": "999000000000001",
"sim_number": "5550000001",
"plate_number": "TEST-001",
"vin": "",
"tail_length": 5,
"min_moving_speed": 6,
"expiration_date": null,
"created_at": "2025-12-03 19:20:07",
"updated_at": "2026-10-05 20:10:58",
"pivot": {
"user_id": 1,
"device_id": 9001,
"group_id": 0,
"active": 1,
"current_driver_id": null,
"timezone_id": null
},
"group_id": 0,
"lastValidLatitude": -17.7733,
"lastValidLongitude": -63.192099999999996,
"time": "05-10-2026 16:11:06",
"course": 208,
"speed": 42
}
}
]
}
]
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción. Los archivos del flujo coinciden con los verificados, salvo dos campos que producción añade (`no_position_update` y `last_position_time`). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Vehículos con posición actualizada desde un instante (delta) + eventos recientes
**GET** `/api/get_devices_latest` · Estado: Verificado
Modo delta: devuelve solo los vehículos cuya posición se actualizó desde el instante `time` (segundos Unix, UTC) y, además, `events` con los eventos recientes (hasta 5 minutos atrás). `time` vacío o `0` se interpreta como "hace 5 segundos"; para obtener todos los vehículos envía `time=1`. Guarda el `time` de la respuesta y úsalo en la consulta siguiente. `sensors`, `services` y `tail` de cada vehículo llegan como cadenas JSON que debes decodificar.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
- Comparte con `get_devices` el riesgo de HTTP 500 si algún vehículo tiene el icono sin asignar.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `time` | query | integer | No | Segundos Unix UTC. Por defecto ahora−5 s. Usa 1 para todo. |
| `device_id` | query | integer | No | Un vehículo. |
| `id` | query | integer | No | Un vehículo. |
| `imei` | query | string | No | IMEI. |
| `limit` | query | integer | No | Vehículos por página (por defecto 100 si se envía page). |
| `page` | query | integer | No | Página. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_devices_latest" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "time=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
time: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_devices_latest?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_devices_latest",
headers={"user-api-hash": API_HASH},
params={"time": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `items` | array de DeviceLatest | |
| `events` | array de object | |
| `time` | integer | Segundos Unix del servidor: úsalo como "time" de la siguiente consulta. |
| `version` | string | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"items": [
{
"id": 9001,
"alarm": 0,
"name": "Camioneta de prueba 01",
"online": "online",
"time": "05-10-2026 16:11:06",
"no_position_update": false,
"last_position_time": "05-10-2026 16:10:58",
"timestamp": 1791231058,
"acktimestamp": 1791231066,
"lat": -17.7733,
"lng": -63.192099999999996,
"course": 208,
"speed": 42,
"altitude": 415,
"icon_type": "rotating",
"icon_color": "green",
"icon_colors": {
"moving": "green",
"stopped": "yellow",
"offline": "red",
"engine": "yellow",
"blocked": "black"
},
"icon": {
"id": 79,
"user_id": null,
"type": "rotating",
"order": null,
"width": 25,
"height": 51,
"path": "images/device_icons/ejemplo_online.png",
"by_status": 1
},
"power": "-",
"address": "-",
"protocol": "gt06",
"driver": "-",
"driver_data": {
"id": null,
"user_id": null,
"device_id": null,
"name": null,
"rfid": null,
"phone": null,
"email": null,
"description": null,
"created_at": null,
"updated_at": null
},
"sensors": "[{\"id\":70001,\"type\":\"battery\",\"name\":\"Bateria\",\"show_in_popup\":1,\"value\":\"87 %\",\"val\":87,\"scale_value\":87,\"tag_name\":\"batterylevel\"},{\"id\":70002,\"type\":\"ignition\",\"name\":\"Ignicion\",\"show_in_popup\":1,\"value\":\"Encendido\",\"val\":true,\"scale_value\":1,\"tag_name\":\"ignition\"}]",
"services": "[{\"id\":80001,\"name\":\"Cambio de aceite\",\"value\":\"Days Left (56d.)\",\"expiring\":false,\"expiration_by\":\"days\"}]",
"tail": "[{\"lat\":\"-17.7811\",\"lng\":\"-63.1843\"},{\"lat\":\"-17.7820\",\"lng\":\"-63.1835\"}]",
"distance_unit_hour": "kph",
"unit_of_distance": "Km",
"unit_of_altitude": "m",
"unit_of_capacity": "L",
"stop_duration": "-",
"stop_duration_sec": 0,
"moved_timestamp": 1791231000,
"engine_status": true,
"detect_engine": "gps",
"engine_hours": "gps",
"total_distance": 12176.23,
"inaccuracy": null,
"sim_expiration_date": "-",
"device_data": {
"id": 9001,
"user_id": 1,
"traccar_device_id": 9001,
"icon_id": 79,
"active": 1,
"enable": 1,
"deleted": 0,
"name": "Camioneta de prueba 01",
"imei": "999000000000001",
"sim_number": "5550000001",
"plate_number": "TEST-001",
"vin": "",
"tail_length": 5,
"min_moving_speed": 6,
"expiration_date": null,
"created_at": "2025-12-03 19:20:07",
"updated_at": "2026-10-05 20:10:58",
"pivot": {
"user_id": 1,
"device_id": 9001,
"group_id": 0,
"active": 1,
"current_driver_id": null,
"timezone_id": null
},
"group_id": 0,
"lastValidLatitude": -17.7733,
"lastValidLongitude": -63.192099999999996,
"time": "05-10-2026 16:11:06",
"course": 208,
"speed": 42
}
}
],
"events": [],
"time": 1791231068,
"version": "3.7.13"
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción. Mismas dos diferencias aditivas que `get_devices`. La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
---
> Esta página en HTML: https://trackerintl.com/devs/referencia/historial-y-rutas
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las respuestas se midieron con 61 posiciones ficticias en un entorno de pruebas. El rango máximo de 31 días es el valor por defecto del código, no una medición de producción.
# Historial y rutas
Recorrido de un vehículo por tramos de conducción y parada, y posiciones GPS crudas paginadas. Rango máximo, zona horaria y ejemplos.
Hay dos formas de consultar el historial de un vehículo:
| Necesitas | Usa |
|---|---|
| El recorrido resumido: tramos de conducción y paradas, distancia y velocidades | `get_history` |
| Las posiciones GPS una por una, con sus atributos, paginadas | `get_history_messages` |
## Tramos del recorrido
En `get_history`, cada elemento de `items` es un tramo. Su campo `status` indica el tipo:
| `status` | Significado |
|---|---|
| `1` | Conducción |
| `2` | Parada |
| `3` | Inicio |
| `4` | Fin |
| `5` | Evento |
Cada tramo trae sus puntos en `items`, con `lat`, `lng`, `speed` y las horas. Los totales del periodo (`distance_sum`, `top_speed`, `move_duration`, `stop_duration`…) vienen con su unidad, como texto.
## Fechas
Las fechas y horas que envías se interpretan en la zona horaria de tu cuenta. En cada posición, `raw_time` y `time` salen en la zona de la cuenta, mientras que `device_time` y `server_time` vienen crudos en UTC. Mira [Convenciones](/devs/convenciones).
## Recorrido de un vehículo por tramos de conducción/parada
**GET** `/api/get_history` · Estado: Verificado
Reconstruye el recorrido de un vehículo en tramos de conducción y de parada. Identifica el vehículo con `device_id` o con `imei`. Las fechas y horas se interpretan en la zona horaria de la cuenta, no en UTC. Rango máximo de 31 días (valor por defecto del código; el de producción no está confirmado). Requiere permiso de historial, que el vehículo sea del usuario y que no esté vencido.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
- Dentro de cada posición, `raw_time` y `time` están en la zona de la cuenta, mientras que `device_time` y `server_time` vienen crudos en UTC.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `device_id` | query | integer | No | Obligatorio si no se envía imei. |
| `imei` | query | string | No | Alternativa a device_id. |
| `from_date` | query | string | Sí | Fecha inicial YYYY-MM-DD. Zona horaria: la de la cuenta del usuario de la API, no UTC. Formato de visualización: el configurado en la cuenta (en las mediciones fue dd-mm-YYYY HH:mm:ss). |
| `from_time` | query | string | Sí | Hora inicial HH:MM o HH:MM:SS (validación: regex). |
| `to_date` | query | string | Sí | Fecha final YYYY-MM-DD. |
| `to_time` | query | string | Sí | Hora final HH:MM o HH:MM:SS. |
| `stops` | query | integer | No | Segundos mínimos para considerar una parada (por defecto 180). |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_history" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "device_id=9001" \
--data-urlencode "from_date=2026-10-01" \
--data-urlencode "from_time=00:00" \
--data-urlencode "to_date=2026-10-02" \
--data-urlencode "to_time=23:59"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
device_id: "9001",
from_date: "2026-10-01",
from_time: "00:00",
to_date: "2026-10-02",
to_time: "23:59",
});
const respuesta = await fetch(`${BASE_URL}/api/get_history?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_history",
headers={"user-api-hash": API_HASH},
params={"device_id": "9001", "from_date": "2026-10-01", "from_time": "00:00", "to_date": "2026-10-02", "to_time": "23:59"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"9001", "from_date" => "2026-10-01", "from_time" => "00:00", "to_date" => "2026-10-02", "to_time" => "23:59"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `404` | Dispositivo inexistente: message "\"Device\" was not found.". |
| `422` | Faltan parámetros, formato de hora inválido, rango > 31 días o vehículo vencido. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `items` | array de HistoryItem | |
| `device` | object | Vehículo consultado, con sus sensores. |
| `distance_sum` | string | Con unidad: "9.2 Km". |
| `top_speed` | string | "31 kph". |
| `move_duration` | string | |
| `stop_duration` | string | |
| `engine_hours` | string | |
| `engine_idle` | string | |
| `engine_work` | string | |
| `fuel_consumption` | string \| null | |
| `sensors` | array de object | |
| `fuel_consumption_arr` | array de | |
| `item_class` | array de object | |
| `images` | array de object | |
| `status` | | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"items": [
{
"status": 3,
"time": null,
"show": "01-10-2026 08:00:00",
"raw_time": "2026-10-01 08:00:00",
"distance": 0,
"driver": null,
"items": [
{
"id": 500,
"device_id": 9001,
"item_id": "i500",
"time": "01-10-2026 08:00:00",
"raw_time": "2026-10-01 08:00:00",
"altitude": 400,
"course": 90,
"speed": 27,
"latitude": -17.78,
"longitude": -63.18,
"lat": -17.78,
"lng": -63.18,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:00:00",
"server_time": "2026-10-01 12:00:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 27
},
{
"id": "altitude",
"value": 400
}
]
}
]
},
{
"status": 1,
"index": 1,
"time": "40min 0s",
"show": "01-10-2026 08:00:00",
"left": "01-10-2026 08:40:00",
"raw_time": "2026-10-01 08:00:00",
"distance": 2.91,
"time_seconds": 2400,
"engine_work": 2280,
"engine_idle": 0,
"engine_hours": 2280,
"fuel_consumption": null,
"top_speed": 27,
"average_speed": 27,
"driver": null,
"items": [
{
"id": 500,
"device_id": 9001,
"item_id": "i500",
"time": "01-10-2026 08:00:00",
"raw_time": "2026-10-01 08:00:00",
"altitude": 400,
"course": 90,
"speed": 27,
"latitude": -17.78,
"longitude": -63.18,
"lat": -17.78,
"lng": -63.18,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:00:00",
"server_time": "2026-10-01 12:00:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 27
},
{
"id": "altitude",
"value": 400
}
]
},
{
"id": 501,
"device_id": 9001,
"item_id": "i501",
"time": "01-10-2026 08:02:00",
"raw_time": "2026-10-01 08:02:00",
"altitude": 401,
"course": 90,
"speed": 27,
"latitude": -17.779,
"longitude": -63.179,
"lat": -17.779,
"lng": -63.179,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:02:00",
"server_time": "2026-10-01 12:02:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 27
},
{
"id": "altitude",
"value": 401
}
]
},
{
"id": 502,
"device_id": 9001,
"item_id": "i502",
"time": "01-10-2026 08:04:00",
"raw_time": "2026-10-01 08:04:00",
"altitude": 402,
"course": 90,
"speed": 27,
"latitude": -17.778000000000002,
"longitude": -63.178,
"lat": -17.778000000000002,
"lng": -63.178,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:04:00",
"server_time": "2026-10-01 12:04:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 27
},
{
"id": "altitude",
"value": 402
}
]
},
{
"id": 503,
"device_id": 9001,
"item_id": "i503",
"time": "01-10-2026 08:06:00",
"raw_time": "2026-10-01 08:06:00",
"altitude": 403,
"course": 90,
"speed": 27,
"latitude": -17.777,
"longitude": -63.177,
"lat": -17.777,
"lng": -63.177,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:06:00",
"server_time": "2026-10-01 12:06:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 27
},
{
"id": "altitude",
"value": 403
}
]
},
{
"id": 504,
"device_id": 9001,
"item_id": "i504",
"time": "01-10-2026 08:08:00",
"raw_time": "2026-10-01 08:08:00",
"altitude": 404,
"course": 90,
"speed": 27,
"latitude": -17.776,
"longitude": -63.176,
"lat": -17.776,
"lng": -63.176,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:08:00",
"server_time": "2026-10-01 12:08:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 27
},
{
"id": "altitude",
"value": 404
}
]
}
]
},
{
"status": 2,
"index": 1,
"time": "30min 0s",
"show": "01-10-2026 08:40:00",
"left": "01-10-2026 09:10:00",
"raw_time": "2026-10-01 08:40:00",
"distance": 0,
"time_seconds": 1800,
"engine_work": 0,
"engine_idle": 0,
"engine_hours": 0,
"fuel_consumption": null,
"top_speed": 0,
"average_speed": 0,
"driver": null,
"items": [
{
"id": 520,
"device_id": 9001,
"item_id": "i520",
"time": "01-10-2026 08:40:00",
"raw_time": "2026-10-01 08:40:00",
"altitude": 420,
"course": 90,
"speed": 0,
"latitude": -17.76,
"longitude": -63.16,
"lat": -17.76,
"lng": -63.16,
"distance": 0,
"other": "0",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:40:00",
"server_time": "2026-10-01 12:40:00",
"other_arr": [
"ignition: 0"
],
"sensors_data": [
{
"id": "speed",
"value": 0
},
{
"id": "altitude",
"value": 420
}
]
},
{
"id": 521,
"device_id": 9001,
"item_id": "i521",
"time": "01-10-2026 08:42:00",
"raw_time": "2026-10-01 08:42:00",
"altitude": 421,
"course": 90,
"speed": 0,
"latitude": -17.759,
"longitude": -63.159,
"lat": -17.759,
"lng": -63.159,
"distance": 0,
"other": "0",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:42:00",
"server_time": "2026-10-01 12:42:00",
"other_arr": [
"ignition: 0"
],
"sensors_data": [
{
"id": "speed",
"value": 0
},
{
"id": "altitude",
"value": 421
}
]
},
{
"id": 522,
"device_id": 9001,
"item_id": "i522",
"time": "01-10-2026 08:44:00",
"raw_time": "2026-10-01 08:44:00",
"altitude": 422,
"course": 90,
"speed": 0,
"latitude": -17.758000000000003,
"longitude": -63.158,
"lat": -17.758000000000003,
"lng": -63.158,
"distance": 0,
"other": "0",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 12:44:00",
"server_time": "2026-10-01 12:44:00",
"other_arr": [
"ignition: 0"
],
"sensors_data": [
{
"id": "speed",
"value": 0
},
{
"id": "altitude",
"value": 422
}
]
}
]
},
{
"status": 1,
"index": 2,
"time": "48min 0s",
"show": "01-10-2026 09:10:00",
"left": "01-10-2026 09:58:00",
"raw_time": "2026-10-01 09:10:00",
"distance": 6.29,
"time_seconds": 2880,
"engine_work": 2880,
"engine_idle": 0,
"engine_hours": 2880,
"fuel_consumption": null,
"top_speed": 31,
"average_speed": 29,
"driver": null,
"items": [
{
"id": 535,
"device_id": 9001,
"item_id": "i535",
"time": "01-10-2026 09:10:00",
"raw_time": "2026-10-01 09:10:00",
"altitude": 435,
"course": 90,
"speed": 31,
"latitude": -17.745,
"longitude": -63.145,
"lat": -17.745,
"lng": -63.145,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 13:10:00",
"server_time": "2026-10-01 13:10:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 31
},
{
"id": "altitude",
"value": 435
}
]
},
{
"id": 536,
"device_id": 9001,
"item_id": "i536",
"time": "01-10-2026 09:12:00",
"raw_time": "2026-10-01 09:12:00",
"altitude": 436,
"course": 90,
"speed": 31,
"latitude": -17.744,
"longitude": -63.144,
"lat": -17.744,
"lng": -63.144,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 13:12:00",
"server_time": "2026-10-01 13:12:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 31
},
{
"id": "altitude",
"value": 436
}
]
},
{
"id": 537,
"device_id": 9001,
"item_id": "i537",
"time": "01-10-2026 09:14:00",
"raw_time": "2026-10-01 09:14:00",
"altitude": 437,
"course": 90,
"speed": 31,
"latitude": -17.743000000000002,
"longitude": -63.143,
"lat": -17.743000000000002,
"lng": -63.143,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 13:14:00",
"server_time": "2026-10-01 13:14:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 31
},
{
"id": "altitude",
"value": 437
}
]
},
{
"id": 538,
"device_id": 9001,
"item_id": "i538",
"time": "01-10-2026 09:16:00",
"raw_time": "2026-10-01 09:16:00",
"altitude": 438,
"course": 90,
"speed": 31,
"latitude": -17.742,
"longitude": -63.142,
"lat": -17.742,
"lng": -63.142,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 13:16:00",
"server_time": "2026-10-01 13:16:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 31
},
{
"id": "altitude",
"value": 438
}
]
}
]
},
{
"status": 4,
"time": null,
"show": "01-10-2026 09:58:00",
"raw_time": "2026-10-01 09:58:00",
"distance": 0,
"driver": null,
"items": [
{
"id": 538,
"device_id": 9001,
"item_id": "i538",
"time": "01-10-2026 09:16:00",
"raw_time": "2026-10-01 09:16:00",
"altitude": 438,
"course": 90,
"speed": 31,
"latitude": -17.742,
"longitude": -63.142,
"lat": -17.742,
"lng": -63.142,
"distance": 0.12,
"other": "1",
"color": "#000000",
"valid": 1,
"device_time": "2026-10-01 13:16:00",
"server_time": "2026-10-01 13:16:00",
"other_arr": [
"ignition: 1"
],
"sensors_data": [
{
"id": "speed",
"value": 31
},
{
"id": "altitude",
"value": 438
}
]
}
]
}
],
"device": {
"id": 9001,
"name": "Camioneta de prueba 01",
"imei": "999000000000001",
"plate_number": "TEST-001",
"sensors": [],
"traccar": {}
},
"distance_sum": "9.2 Km",
"top_speed": "31 kph",
"move_duration": "1h 28min 0s",
"stop_duration": "30min 0s",
"engine_hours": "1h 28min 0s",
"engine_idle": "0s",
"engine_work": "1h 28min 0s",
"fuel_consumption": null,
"sensors": [
{
"id": "speed",
"name": "Speed",
"sufix": "kph"
},
{
"id": "altitude",
"name": "Altitude",
"sufix": "m"
}
],
"fuel_consumption_arr": [],
"item_class": [
{
"id": 1,
"value": "drive",
"title": "Drive"
},
{
"id": 2,
"value": "stop",
"title": "Stop"
},
{
"id": 3,
"value": "start",
"title": "Start"
},
{
"id": 4,
"value": "end",
"title": "End"
},
{
"id": 5,
"value": "event",
"title": "Event"
}
],
"images": [
{
"id": 1,
"value": "https://example.invalid/assets/images/route_drive.png",
"title": "https://example.invalid/assets/images/route_drive.png"
}
],
"status": 1
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Posiciones GPS crudas paginadas
**GET** `/api/get_history_messages` · Estado: Verificado
Lista las posiciones almacenadas de un vehículo en el rango dado, paginadas. `limit` solo admite 50, 100, 200, 300, 400, 500 o 1000; cualquier otro valor se sustituye por 50 sin avisar. `sorting=true` ordena de la más reciente a la más antigua. A diferencia de `get_history`, no aplica el límite de 31 días y exige `device_id` (no acepta `imei`).
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
- Aunque este endpoint no limita el rango, conviene consultar por tramos de hasta 31 días, igual que `get_history`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `device_id` | query | integer | Sí | ID del vehículo (campo id de get_devices). |
| `from_date` | query | string | Sí | Fecha inicial YYYY-MM-DD. Zona horaria: la de la cuenta del usuario de la API, no UTC. Formato de visualización: el configurado en la cuenta (en las mediciones fue dd-mm-YYYY HH:mm:ss). |
| `from_time` | query | string | Sí | Hora inicial HH:MM o HH:MM:SS (validación: regex). |
| `to_date` | query | string | Sí | Fecha final YYYY-MM-DD. |
| `to_time` | query | string | Sí | Hora final HH:MM o HH:MM:SS. |
| `limit` | query | integer (50, 100, 200, 300, 400, 500, 1000) | No | Tamaño de página (valor distinto -> 50). |
| `page` | query | integer | No | Número de página. |
| `sorting` | query | string ("true") | No | true = descendente. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_history_messages" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "device_id=9001" \
--data-urlencode "from_date=2026-10-01" \
--data-urlencode "from_time=00:00" \
--data-urlencode "to_date=2026-10-02" \
--data-urlencode "to_time=23:59" \
--data-urlencode "limit=100" \
--data-urlencode "page=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
device_id: "9001",
from_date: "2026-10-01",
from_time: "00:00",
to_date: "2026-10-02",
to_time: "23:59",
limit: "100",
page: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_history_messages?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_history_messages",
headers={"user-api-hash": API_HASH},
params={"device_id": "9001", "from_date": "2026-10-01", "from_time": "00:00", "to_date": "2026-10-02", "to_time": "23:59", "limit": "100", "page": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"9001", "from_date" => "2026-10-01", "from_time" => "00:00", "to_date" => "2026-10-02", "to_time" => "23:59", "limit" => "100", "page" => "1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `404` | Dispositivo inexistente. |
| `422` | Faltan device_id o fechas. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `messages` | Paginator + object | |
| `sensors` | array de object | |
| `pagination_limits` | object | |
| `limit` | integer | |
| `sorting` | string ("asc", "desc") | |
| `parameters` | object | |
| `status` | | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"messages": {
"current_page": 1,
"data": [
{
"id": 1000,
"altitude": 400,
"course": 90,
"latitude": -17.78,
"longitude": -63.18,
"other": "11000",
"speed": "27.00",
"time": "01-10-2026 08:00:00",
"device_time": "2026-10-01 12:00:00",
"server_time": "01-10-2026 08:00:00",
"sensors_values": null,
"valid": 1,
"distance": 120,
"protocol": "gt06",
"device_id": 9001,
"other_arr": [
"ignition: 1",
"totaldistance: 1000"
],
"other_array": {
"ignition": "1",
"totaldistance": "1000"
},
"popup_sensors": {
"70001": {
"name": "Bateria",
"value": "87 %"
}
},
"sensors_value": {
"70001": "87 %"
}
},
{
"id": 1001,
"altitude": 401,
"course": 90,
"latitude": -17.779,
"longitude": -63.179,
"other": "11000",
"speed": "27.00",
"time": "01-10-2026 08:01:00",
"device_time": "2026-10-01 12:01:00",
"server_time": "01-10-2026 08:01:00",
"sensors_values": null,
"valid": 1,
"distance": 120,
"protocol": "gt06",
"device_id": 9001,
"other_arr": [
"ignition: 1",
"totaldistance: 1000"
],
"other_array": {
"ignition": "1",
"totaldistance": "1000"
},
"popup_sensors": {
"70001": {
"name": "Bateria",
"value": "87 %"
}
},
"sensors_value": {
"70001": "87 %"
}
}
],
"first_page_url": "https://plataforma.example.invalid/api/get_history_messages?page=1",
"from": 1,
"last_page": 3,
"last_page_url": "https://plataforma.example.invalid/api/get_history_messages?page=3",
"links": [],
"next_page_url": "https://plataforma.example.invalid/api/get_history_messages?page=2",
"path": "https://plataforma.example.invalid/api/get_history_messages",
"per_page": 50,
"prev_page_url": null,
"to": 2,
"total": 120,
"url": "https://plataforma.example.invalid/api/get_history_messages"
},
"sensors": [],
"pagination_limits": {
"50": 50,
"100": 100,
"200": 200,
"300": 300,
"400": 400,
"500": 500,
"1000": 1000
},
"limit": 50,
"sorting": "asc",
"parameters": {
"ignition": "",
"totaldistance": ""
},
"status": 1
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
---
> Esta página en HTML: https://trackerintl.com/devs/referencia/eventos-y-alertas
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las respuestas se midieron con datos ficticios en un entorno de pruebas. La forma de los horarios de una alerta (`schedules`) no se midió.
# Eventos y alertas
Reglas de alerta, eventos que dispararon y conteo por tipo. Paginación por cursor o por página, filtros y zona horaria.
Una **alerta** es una regla configurada en la plataforma (por ejemplo, exceso de velocidad o entrada a una geocerca). Un **evento** es cada vez que una regla se dispara.
| Necesitas | Usa |
|---|---|
| Las reglas de alerta del usuario | `get_alerts` |
| Los eventos que ocurrieron, del más reciente al más antiguo | `get_events` |
| Cuántos eventos hubo de cada tipo | `get_alerts_summary` |
El campo `type` de un evento identifica la regla que lo generó: por ejemplo `overspeed`, `zone_in`, `zone_out`, `sos`, `ignition_on`, `ignition_off`, `move_start`, `stop_duration`, `offline_duration` o `custom`.
## Paginación de eventos
Sin `page`, `get_events` pagina por cursor: la respuesta trae `next_cursor` y lo envías como `cursor` en la llamada siguiente, hasta que `next_cursor` sea `null`. Con `page`, la respuesta trae totales. No combines ambos. Una fecha inválida en `date_from` o `date_to` no produce error: la API devuelve los datos sin filtrar, así que valida tus fechas antes de enviarlas.
## Eventos generados por las alertas (más recientes primero)
**GET** `/api/get_events` · Estado: Verificado
Lista los eventos generados por las reglas de alerta, del más reciente al más antiguo. Sin `page` usa paginación por cursor (`items.data` y `items.next_cursor`); con `page` devuelve totales. `limit` vale 30 por defecto y 1000 como máximo. Las fechas se interpretan en la zona horaria de la cuenta; una fecha inválida no produce error y devuelve los datos sin filtrar. Requiere permiso para ver eventos.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `device_id` | query | integer | No | |
| `alert_id` | query | integer | No | |
| `type` | query | string | No | Tipo de evento (overspeed, zone_in…). |
| `date_from` | query | string | No | YYYY-MM-DD HH:MM:SS (zona de la cuenta). |
| `date_to` | query | string | No | |
| `created_from` | query | string | No | Sobre created_at. |
| `created_to` | query | string | No | |
| `search` | query | string | No | Mensaje o nombre del vehículo. |
| `limit` | query | integer | No | |
| `page` | query | integer | No | Número de página. |
| `cursor` | query | string | No | next_cursor de la respuesta anterior (solo sin page). |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_events" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "limit=50"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
limit: "50",
});
const respuesta = await fetch(`${BASE_URL}/api/get_events?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_events",
headers={"user-api-hash": API_HASH},
params={"limit": "50"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"50"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `items` | Paginator + object \| CursorPaginator + object | Con ?page= devuelve Paginator; sin page, CursorPaginator. |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"items": {
"data": [
{
"id": 5000,
"user_id": 1,
"device_id": 9001,
"geofence_id": null,
"poi_id": null,
"position_id": 700,
"alert_id": 12,
"type": "overspeed",
"message": "Exceso de velocidad (ficticio)",
"address": null,
"altitude": 0,
"course": 76,
"latitude": -17.78,
"longitude": -63.18,
"power": null,
"speed": 41,
"time": "01-10-2026 17:08:19",
"deleted": 0,
"created_at": "2026-10-01 21:08:21",
"updated_at": "2026-10-01 21:08:21",
"additional": {
"overspeed_speed": 30
},
"silent": null,
"name": "Exceso de velocidad",
"detail": "Velocidad 41 kph, limite 30 kph",
"geofence": null,
"device_name": "Camioneta de prueba 01"
}
],
"path": "https://plataforma.example.invalid/api/get_events",
"per_page": 30,
"next_cursor": "eyJldmVudHMuaWQiOjQ5OTl9",
"next_page_url": "https://plataforma.example.invalid/api/get_events?cursor=eyJldmVudHMuaWQiOjQ5OTl9",
"prev_cursor": null,
"prev_page_url": null
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Reglas de alerta del usuario
**GET** `/api/get_alerts` · Estado: Verificado
Lista las reglas de alerta del usuario (no los eventos que dispararon). Si el usuario no tiene permiso para ver alertas devuelve `items.alerts` vacío, con HTTP 200.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_alerts" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_alerts`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_alerts",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `items` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"items": {
"alerts": [
{
"id": 12,
"user_id": 1,
"active": 1,
"name": "Exceso de velocidad (ficticio)",
"type": "overspeed",
"for_all_user_devices": 0,
"schedules": null,
"notifications": {
"color": {
"active": "0"
},
"silent": {
"active": "0",
"input": "0"
},
"sound": {
"active": "1",
"input": "alarm1"
},
"popup": {
"active": "1",
"input": "10"
},
"push": {
"active": "1"
},
"email": {
"active": "0"
},
"webhook": {
"active": "0"
},
"sharing_email": {
"active": "0"
}
},
"created_at": "2026-02-24 21:09:52",
"updated_at": "2026-02-24 21:09:52",
"zone": 0,
"schedule": 0,
"command": {
"active": "0",
"type": "custom"
},
"continuous_duration": 0,
"devices": [
9001,
9002
],
"drivers": [],
"geofences": [],
"events_custom": [],
"overspeed": 30
}
]
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Conteo de eventos por tipo de alerta
**GET** `/api/get_alerts_summary` · Estado: Verificado
Cuenta los eventos agrupados por tipo. `date_from` y `date_to` se comparan sin conversión de zona horaria (UTC) contra la fecha de creación del evento. Solo acepta GET y requiere permiso para ver eventos.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `date_from` | query | string | No | created_at >= (UTC). |
| `date_to` | query | string | No | created_at <= (UTC). |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_alerts_summary" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "date_from=2026-10-01" \
--data-urlencode "date_to=2026-10-05"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
date_from: "2026-10-01",
date_to: "2026-10-05",
});
const respuesta = await fetch(`${BASE_URL}/api/get_alerts_summary?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_alerts_summary",
headers={"user-api-hash": API_HASH},
params={"date_from": "2026-10-01", "date_to": "2026-10-05"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"2026-10-01", "date_to" => "2026-10-05"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `items` | array de object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"items": [
{
"type": "overspeed",
"title": "Overspeed",
"count": 18
},
{
"type": "zone_in",
"title": "Geofence in",
"count": 4
}
]
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
---
> Esta página en HTML: https://trackerintl.com/devs/referencia/geocercas
> Estado: Parcial, revisada el 2026-10-05. Rutas y código confirmados en producción por diff. La respuesta 200 de dos operaciones solo se leyó del código y la forma de las rutas planificadas no se midió. No hay operaciones de creación o edición documentadas.
# Geocercas
Consulta tus geocercas y rutas planificadas, y averigua qué geocercas contienen un punto o la posición actual de un vehículo.
Esta sección es de **lectura**: lista geocercas y rutas, y responde en qué geocercas está un punto o un vehículo. Crear, editar o eliminar geocercas no está documentado.
- Una **geocerca** es un polígono o un círculo (`type`) que dibujó el usuario. Sus coordenadas llegan en `coordinates` como una **cadena JSON** que debes decodificar.
- Una **ruta planificada** es una línea de referencia dibujada por el usuario; no es el recorrido real. Para el recorrido real usa [Historial y rutas](/devs/referencia/historial-y-rutas).
- `point_in_geofences` y `devices_in_geofences` devuelven los **nombres** de las geocercas que contienen el punto. Tienen límites de peticiones por minuto (mira [Límites](/devs/limites)).
## Geocercas del usuario
**GET** `/api/get_geofences` · Estado: Verificado
Lista los polígonos y círculos propios del usuario. `coordinates` es una cadena JSON que debes decodificar. Si el usuario no tiene permiso, devuelve `items.geofences` vacío.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `search` | query | string | No | |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_geofences" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_geofences`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_geofences",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `items` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"items": {
"geofences": [
{
"id": 301,
"user_id": 1,
"group_id": 0,
"active": 1,
"name": "Base ficticia",
"coordinates": "[{\"lat\":-17.78,\"lng\":-63.19},{\"lat\":-17.78,\"lng\":-63.17},{\"lat\":-17.79,\"lng\":-63.17},{\"lat\":-17.79,\"lng\":-63.19}]",
"polygon_color": "#d000df",
"created_at": "2026-02-24 21:09:52",
"updated_at": "2026-02-24 21:09:52",
"type": "polygon",
"radius": null,
"center": null,
"device_id": null,
"speed_limit": 0,
"diem_rate_id": null,
"additional": null
}
]
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Geocercas que contienen un punto
**GET** `/api/point_in_geofences` · Estado: Parcial
Devuelve los nombres de las geocercas del usuario que contienen el punto (`lat`, `lng`). Límite de 30 peticiones por minuto. Si faltan `lat` o `lng`, responde HTTP 200 con `{status: 0, errors}`; si el usuario no tiene geocercas, responde 404.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`. Límite de la plataforma: 30 peticiones por minuto.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `lat` | query | number | Sí | Latitud. |
| `lng` | query | number | Sí | Longitud. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/point_in_geofences" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "lat=-17.78" \
--data-urlencode "lng=-63.18"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
lat: "-17.78",
lng: "-63.18",
});
const respuesta = await fetch(`${BASE_URL}/api/point_in_geofences?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/point_in_geofences",
headers={"user-api-hash": API_HASH},
params={"lat": "-17.78", "lng": "-63.18"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"-17.78", "lng" => "-63.18"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | OK, o error de validación con HTTP 200. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `404` | El usuario no tiene geocercas. |
| `429` | Límite de peticiones superado (inferido del comportamiento estándar; no medido). |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"zones": [
"Base ficticia"
]
}
```
### Verificación
Estado: Parcial. Revisado el 5 de octubre de 2026. Ruta y código confirmados en producción. La validación y el límite de peticiones se midieron en un entorno de pruebas; la respuesta 200 solo se leyó del código porque ese entorno no podía calcularla.
## Geocercas que contienen la posición actual de un vehículo
**GET** `/api/devices_in_geofences` · Estado: Parcial
Igual que `point_in_geofences`, pero usa la última posición de un vehículo del usuario. Límite de 1800 peticiones por minuto. Responde 404 si el usuario no tiene geocercas.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`. Límite de la plataforma: 1800 peticiones por minuto.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `device_id` | query | integer | Sí | ID del vehículo (campo id de get_devices). |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/devices_in_geofences" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "device_id=9001"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
device_id: "9001",
});
const respuesta = await fetch(`${BASE_URL}/api/devices_in_geofences?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/devices_in_geofences",
headers={"user-api-hash": API_HASH},
params={"device_id": "9001"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"9001"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `404` | El usuario no tiene geocercas. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `zones` | array de string | Nombres de las geocercas que contienen el punto. |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"zones": [
"Base ficticia"
]
}
```
### Verificación
Estado: Parcial. Revisado el 5 de octubre de 2026. Ruta y código confirmados en producción. La respuesta 200 no se midió; se leyó del código.
## Rutas planificadas
**GET** `/api/get_routes` · Estado: Parcial
Lista las rutas de referencia que dibujó el usuario (no es el recorrido real), 500 por página. Requiere permiso para ver rutas.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `search` | query | string | No | |
| `page` | query | integer | No | Número de página. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_routes" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_routes`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_routes",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `data` | array de object | |
| `pagination` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"data": [],
"pagination": {
"total": 0,
"per_page": 500,
"current_page": 1,
"last_page": 1,
"next_page_url": null,
"prev_page_url": null
}
}
```
### Verificación
Estado: Parcial. Revisado el 5 de octubre de 2026. Ruta confirmada en producción. La forma de `data[]` no se midió porque el entorno de pruebas no tenía rutas.
---
> Esta página en HTML: https://trackerintl.com/devs/referencia/reportes
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las respuestas se midieron con datos ficticios en un entorno de pruebas. Solo se documentan consultas: generar o descargar reportes queda fuera.
# Reportes
Catálogo de tipos de reporte de tu cuenta y plantillas guardadas. La generación de reportes no está documentada.
Estas operaciones **no generan reportes**. Sirven para saber qué tipos de reporte puede generar tu cuenta y qué plantillas guardó el usuario. La generación y descarga de reportes todavía no está documentada.
## Plantillas de reporte guardadas
**GET** `/api/get_reports` · Estado: Verificado
Lista las plantillas de reporte guardadas por el usuario. No genera reportes. `limit` vale 10 por defecto.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `limit` | query | integer | No | |
| `page` | query | integer | No | Número de página. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_reports" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "limit=10"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
limit: "10",
});
const respuesta = await fetch(`${BASE_URL}/api/get_reports?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_reports",
headers={"user-api-hash": API_HASH},
params={"limit": "10"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"10"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `items` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"items": {
"reports": {
"current_page": 1,
"data": [],
"first_page_url": "https://plataforma.example.invalid/api/get_reports?page=1",
"from": null,
"last_page": 1,
"last_page_url": "https://plataforma.example.invalid/api/get_reports?page=1",
"links": [],
"next_page_url": null,
"path": "https://plataforma.example.invalid/api/get_reports",
"per_page": 10,
"prev_page_url": null,
"to": null,
"total": 0
},
"types": [
{
"id": 1,
"title": "General information"
},
{
"id": 2,
"title": "Stops"
}
]
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Tipos de reporte habilitados para la cuenta
**GET** `/api/get_reports_types` · Estado: Verificado
Catálogo de los tipos de reporte disponibles para la cuenta: id, nombre, formatos, campos y reglas de validación. La respuesta es grande: unos 128 KB en una cuenta de pruebas con 79 tipos.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_reports_types" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_reports_types`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_reports_types",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `items` | array de ReportType | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"items": [
{
"type": 1,
"name": "General information",
"formats": [
"html",
"xls",
"pdf",
"pdf_land",
"json",
"csv"
],
"fields": [
"date_from",
"date_to"
],
"validation": {
"type": "required"
},
"parameters": []
},
{
"type": 2,
"name": "Stops",
"formats": [
"html",
"xls",
"pdf"
],
"fields": [
"date_from",
"date_to",
"stops"
],
"validation": {
"type": "required"
},
"parameters": []
}
]
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
---
> Esta página en HTML: https://trackerintl.com/devs/referencia/usuario
> Estado: Verificado, revisada el 2026-10-05. Verificado por lectura del código y diff contra producción; las respuestas se midieron con datos ficticios en un entorno de pruebas. No probado con una cuenta real.
# Usuario
Datos y permisos del usuario de la API, sus conductores y el historial de comandos enviados a los equipos.
`get_user_data` es la primera llamada que conviene hacer con un hash nuevo: confirma que la credencial funciona y muestra qué módulos puede ver, editar o eliminar el usuario. Las otras dos operaciones consultan conductores y el historial de comandos enviados; ninguna envía datos.
En `get_user_data`, `group_id` identifica el tipo de cuenta (1 administrador, 2 usuario o cliente, 3 gerente o revendedor, 4 demo, 5 operador, 6 supervisor y 7 financiero, este último solo en producción).
## Datos y permisos del usuario de la API
**GET** `/api/get_user_data` · Estado: Verificado
Devuelve el correo, el plan, el vencimiento de la suscripción, los días restantes, el límite de vehículos y la matriz de permisos del usuario. Úsalo para comprobar que la credencial funciona.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_user_data" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_user_data`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_user_data",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `email` | string | |
| `expiration_date` | string \| null | |
| `days_left` | integer \| null | |
| `plan` | string | |
| `devices_limit` | integer | |
| `group_id` | integer | 1 admin, 2 usuario/cliente, 3 gerente (reseller), 4 demo, 5 operador, 6 supervisor; 7 = financiero (solo en producción). |
| `role_id` | integer | |
| `permissions` | object | Módulo -> {view,edit,remove}. Incluye claves como "devices", "history", "device.imei". |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"email": "demo@example.invalid",
"expiration_date": "31-12-2030 23:59:59",
"days_left": 1547,
"plan": "Plan de demostracion (ficticio)",
"devices_limit": 10,
"group_id": 2,
"role_id": 2,
"permissions": {
"devices": {
"view": true,
"edit": false,
"remove": false
},
"alerts": {
"view": true,
"edit": false,
"remove": false
},
"events": {
"view": true,
"edit": false,
"remove": false
},
"geofences": {
"view": true,
"edit": false,
"remove": false
},
"routes": {
"view": true,
"edit": false,
"remove": false
},
"reports": {
"view": true,
"edit": false,
"remove": false
},
"history": {
"view": true,
"edit": false,
"remove": false
},
"send_command": {
"view": false,
"edit": false,
"remove": false
}
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Conductores del usuario
**GET** `/api/get_user_drivers` · Estado: Verificado
Lista los conductores del usuario, paginados (`limit` vale 10 por defecto). Requiere permiso para ver conductores.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `limit` | query | integer | No | |
| `search` | query | string | No | |
| `page` | query | integer | No | Número de página. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/get_user_drivers" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/get_user_drivers`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_user_drivers",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `status` | | |
| `items` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"status": 1,
"items": {
"drivers": {
"current_page": 1,
"data": [
{
"id": 21,
"user_id": 1,
"device_id": null,
"name": "Conductor de prueba",
"rfid": "0000000001",
"phone": "5550000000",
"email": "conductor@example.invalid",
"description": null,
"created_at": "2026-01-01 00:00:00",
"updated_at": "2026-01-01 00:00:00"
}
],
"first_page_url": "https://plataforma.example.invalid/api/get_user_drivers?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://plataforma.example.invalid/api/get_user_drivers?page=1",
"links": [],
"next_page_url": null,
"path": "https://plataforma.example.invalid/api/get_user_drivers",
"per_page": 10,
"prev_page_url": null,
"to": 1,
"total": 1
}
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
## Historial de comandos enviados a los equipos
**GET** `/api/sent_commands` · Estado: Verificado
Historial de los comandos enviados a los equipos, 15 por página. Es solo lectura: no envía ningún comando. Requiere permiso para ver comandos.
Autenticación. Cabecera `user-api-hash` con tu `user_api_hash`.
### Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
| `search` | query | string | No | |
| `page` | query | integer | No | Número de página. |
| `lang` | query | string | No | Idioma de los textos traducibles (SetLang). También se admite la cabecera Accept-Language. |
### Ejemplo de solicitud
cURL:
```bash
curl -sS "https://plataforma.trackerintl.com/api/sent_commands" \
-H "user-api-hash: TU_API_HASH"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const respuesta = await fetch(`${BASE_URL}/api/sent_commands`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/sent_commands",
headers={"user-api-hash": API_HASH},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
### Respuestas
| Código | Descripción |
|---|---|
| `200` | Respuesta correcta. |
| `401` | Hash ausente o incorrecto, usuario inactivo, suscripción vencida (también la del gerente) o API deshabilitada por falta de pago del servidor. HTTP 401 con `{status: 0, message}`. |
| `403` | Sin permiso del rol para el módulo, vehículo de otro usuario o cuenta restringida por facturación. Incluye `perm: 0`. |
| `500` | Error interno de la plataforma. Con la depuración apagada el texto del mensaje es genérico. |
| Campo (respuesta 200) | Tipo | Descripción |
|---|---|---|
| `data` | array de SentCommand | |
| `pagination` | object | |
Ejemplo de respuesta 200 (datos ficticios):
```json
{
"data": [
{
"id": 401,
"user_id": 1,
"imei": "999000000000001",
"connection": "gprs",
"command_title": "Posicion unica (ficticio)",
"parameters": {
"data": "WHERE#"
},
"response": "OK",
"status": 1,
"created_at": "01-10-2026 09:00:00",
"updated_at": "01-10-2026 09:00:05"
}
],
"pagination": {
"total": 1,
"per_page": 15,
"current_page": 1,
"last_page": 1,
"next_page_url": null,
"prev_page_url": null
}
}
```
### Verificación
Estado: Verificado. Revisado el 5 de octubre de 2026. Ruta confirmada en producción y archivos del flujo idénticos a los verificados (comparación por huella y diff). La forma de la respuesta se midió con datos ficticios en un entorno de pruebas; no se probó con una cuenta real de producción.
---
> Esta página en HTML: https://trackerintl.com/devs/guias
> Estado: Parcial, revisada el 2026-10-05. Las guías usan solo operaciones verificadas. Los scripts se ejecutaron contra un servidor simulado con datos ficticios; todavía no se han ejecutado contra una cuenta real.
# Guías
Guías de integración con lo que la API de lectura permite hoy: sincronizar con un ERP o CRM, seguir posiciones en vivo y consultar historial por periodos.
Estas guías combinan varias operaciones de la [referencia](/devs/referencia) para resolver una tarea. Todas funcionan por **lectura periódica**: tu sistema consulta la plataforma cada cierto tiempo. No hay webhooks documentados (mira [Webhooks](/devs/referencia/webhooks)).
- [Sincronizar con un ERP o CRM](https://trackerintl.com/devs/guias/erp-y-crm): Mantén tu ERP o CRM al día con tus vehículos y los eventos de alertas mediante lectura periódica de la API, con un script de ejemplo con cursor.
- [Posición en vivo](https://trackerintl.com/devs/guias/posicion-en-vivo): Sigue las posiciones de tu flota con get_devices_latest en modo delta: qué enviar, qué guardar y cómo calcular el vehículo más cercano en tu sistema.
- [Historial de recorridos](https://trackerintl.com/devs/guias/historial-de-recorridos): Consulta el recorrido de un vehículo por periodos de hasta 31 días, con tramos de conducción y parada o posiciones GPS crudas paginadas.
## Lo que la API no permite hoy
Algunas integraciones que se suelen pedir **no están respaldadas** por la API de lectura y por eso no tienen guía:
- **Pasarelas de pago y cobros**: la API documentada no maneja cobros ni suscripciones.
- **Ingesta de lecturas de sensores externos**: la API documentada no recibe datos; solo los consulta.
- **Órdenes de mantenimiento automáticas**: no hay operaciones documentadas para crearlas. Puedes leer los servicios de cada vehículo dentro de `get_devices`, pero la lógica de las órdenes es de tu sistema.
- **Asignación automática de la unidad más cercana**: la API entrega las coordenadas de cada vehículo; el cálculo de distancias lo haces tú. Mira [Posición en vivo](/devs/guias/posicion-en-vivo).
Si necesitas alguna de ellas, [cuéntanos tu caso](/contacto).
---
> Esta página en HTML: https://trackerintl.com/devs/guias/erp-y-crm
> Estado: Parcial, revisada el 2026-10-05. Usa operaciones verificadas por diff contra producción. El script se ejecutó contra un servidor simulado con 95 eventos ficticios; no se ha ejecutado contra una cuenta real.
# Sincronizar con un ERP o CRM
Mantén tu ERP o CRM al día con tus vehículos y los eventos de alertas mediante lectura periódica de la API, con un script de ejemplo con cursor.
Esta guía sincroniza dos cosas con un sistema externo: el **inventario de vehículos** (cada cierto tiempo) y los **eventos de alertas** (de forma incremental). La API entrega los datos; el modelo de tu ERP o CRM y su lógica los defines tú.
## Plan
1. **Inventario**: llama a `get_devices` con `limit` y `page` hasta que la lista llegue vacía. Relaciona cada `id` con tu activo; los datos del equipo (IMEI, placa) están en `device_data`.
2. **Eventos**: llama a `get_events` sin `page`. La respuesta trae `items.data` y `items.next_cursor`; envía `next_cursor` como `cursor` hasta que sea `null`.
3. **Incremental**: en las ejecuciones siguientes usa `date_from` con la fecha del último evento que procesaste, para no recorrer todo. Las fechas se interpretan en la zona horaria de la cuenta.
4. **Idempotencia**: usa el `id` del evento como clave única en tu sistema. Si una ejecución se repite, no duplicarás registros.
5. **Frecuencia**: no hay una frecuencia oficial. Respeta los [límites](/devs/limites) y reintenta con espera creciente si recibes un `429` o un `5xx`.
## Inventario de vehículos
**Listar vehículos**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_devices" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "limit=50" \
--data-urlencode "page=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
limit: "50",
page: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_devices?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_devices",
headers={"user-api-hash": API_HASH},
params={"limit": "50", "page": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"50", "page" => "1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
## Eventos con cursor
**Eventos recientes**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_events" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "limit=50"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
limit: "50",
});
const respuesta = await fetch(`${BASE_URL}/api/get_events?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_events",
headers={"user-api-hash": API_HASH},
params={"limit": "50"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"50"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
## Script de ejemplo
Este script lee todos los eventos disponibles siguiendo el cursor y muestra cuántos leyó. Sustituye la parte final por la escritura en tu sistema.
**Leer eventos con cursor**
cURL:
```bash
#!/usr/bin/env bash
# Requiere curl y jq.
BASE_URL="https://plataforma.trackerintl.com"
API_HASH="TU_API_HASH"
CURSOR=""
TOTAL=0
while true; do
RESPUESTA=$(curl -sS -G "$BASE_URL/api/get_events" \
-H "user-api-hash: $API_HASH" \
--data-urlencode "limit=50" \
--data-urlencode "cursor=$CURSOR")
# Aquí guardas los eventos en tu sistema, usando .id como clave única.
TOTAL=$((TOTAL + $(echo "$RESPUESTA" | jq '.items.data | length')))
CURSOR=$(echo "$RESPUESTA" | jq -r '.items.next_cursor // empty')
[ -z "$CURSOR" ] && break
done
echo "Eventos leídos: $TOTAL"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
let cursor = null;
let total = 0;
do {
const params = new URLSearchParams({ limit: "50" });
if (cursor) params.set("cursor", cursor);
const respuesta = await fetch(`${BASE_URL}/api/get_events?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}`);
const { items } = await respuesta.json();
for (const evento of items.data) {
// Aquí guardas el evento en tu sistema, usando evento.id como clave única.
total += 1;
}
cursor = items.next_cursor;
} while (cursor);
console.log(`Eventos leídos: ${total}`);
```
Python:
```python
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
cursor = None
total = 0
while True:
params = {"limit": "50"}
if cursor:
params["cursor"] = cursor
respuesta = requests.get(
f"{BASE_URL}/api/get_events",
headers={"user-api-hash": API_HASH},
params=params,
timeout=30,
)
respuesta.raise_for_status()
items = respuesta.json()["items"]
for evento in items["data"]:
# Aquí guardas el evento en tu sistema, usando evento["id"] como clave única.
total += 1
cursor = items["next_cursor"]
if not cursor:
break
print(f"Eventos leídos: {total}")
```
PHP:
```php
"50"];
if ($cursor) {
$params["cursor"] = $cursor;
}
$ch = curl_init($baseUrl . "/api/get_events?" . http_build_query($params));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
$items = json_decode($cuerpo, true)["items"];
foreach ($items["data"] as $evento) {
// Aquí guardas el evento en tu sistema, usando $evento["id"] como clave única.
$total++;
}
$cursor = $items["next_cursor"];
} while ($cursor);
echo "Eventos leídos: $total\n";
```
> **Sobre los datos.**
>
> El script del ejemplo no guarda estado entre ejecuciones. En tu integración, guarda la fecha del último evento procesado y el `id` para la idempotencia.
---
> Esta página en HTML: https://trackerintl.com/devs/guias/posicion-en-vivo
> Estado: Parcial, revisada el 2026-10-05. Usa operaciones verificadas por diff contra producción. El script se ejecutó contra un servidor simulado; la latencia real de actualización de posiciones no se midió y no se publica.
# Posición en vivo
Sigue las posiciones de tu flota con get_devices_latest en modo delta: qué enviar, qué guardar y cómo calcular el vehículo más cercano en tu sistema.
`get_devices_latest` está pensado para sondeo: en lugar de pedir toda la flota cada vez, le dices desde qué instante quieres cambios y devuelve solo los vehículos con posición nueva.
## Cómo funciona
1. Primera llamada con `time=1`: devuelve **todos** los vehículos. (`time` vacío o `0` equivale a "hace 5 segundos", así que no uses `0`).
2. Guarda el valor `time` de la respuesta.
3. En la siguiente llamada envía ese valor como `time`: recibes solo los vehículos con posición actualizada desde entonces.
4. Repite. La respuesta también trae `events` con los eventos recientes (hasta 5 minutos atrás).
Las coordenadas de cada vehículo son `lat` y `lng`. Los campos `sensors`, `services` y `tail` llegan como cadenas JSON que debes decodificar.
## Solicitud
**Posiciones actualizadas desde un instante**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_devices_latest" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "time=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
time: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_devices_latest?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_devices_latest",
headers={"user-api-hash": API_HASH},
params={"time": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
## Script de sondeo
El script hace dos rondas, con una espera corta entre ellas, y muestra cuántos vehículos recibió en cada una. En la segunda ronda llegan menos, porque solo se piden los cambios.
**Sondeo en modo delta**
cURL:
```bash
#!/usr/bin/env bash
# Requiere curl y jq.
BASE_URL="https://plataforma.trackerintl.com"
API_HASH="TU_API_HASH"
INTERVALO_SEGUNDOS=1 # en producción, elige un intervalo acorde a tu caso
DESDE=1 # 1 = todos los vehículos en la primera ronda
for RONDA in 1 2; do
DATOS=$(curl -sS -G "$BASE_URL/api/get_devices_latest" \
-H "user-api-hash: $API_HASH" \
--data-urlencode "time=$DESDE")
echo "Ronda $RONDA: $(echo "$DATOS" | jq '.items | length') vehículos con posición nueva"
echo "$DATOS" | jq -r '.items[] | " \(.name): \(.lat), \(.lng)"'
DESDE=$(echo "$DATOS" | jq -r '.time') # la próxima ronda pide solo lo que cambió desde aquí
sleep "$INTERVALO_SEGUNDOS"
done
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const INTERVALO_MS = 1000; // en producción, elige un intervalo acorde a tu caso
const dormir = (ms) => new Promise((resolver) => setTimeout(resolver, ms));
let desde = 1; // 1 = todos los vehículos en la primera ronda
for (let ronda = 1; ronda <= 2; ronda += 1) {
const params = new URLSearchParams({ time: String(desde) });
const respuesta = await fetch(`${BASE_URL}/api/get_devices_latest?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}`);
const datos = await respuesta.json();
console.log(`Ronda ${ronda}: ${datos.items.length} vehículos con posición nueva`);
for (const vehiculo of datos.items) {
console.log(` ${vehiculo.name}: ${vehiculo.lat}, ${vehiculo.lng}`);
}
desde = datos.time; // la próxima ronda pide solo lo que cambió desde aquí
await dormir(INTERVALO_MS);
}
```
Python:
```python
import time
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
INTERVALO_SEGUNDOS = 1 # en producción, elige un intervalo acorde a tu caso
desde = 1 # 1 = todos los vehículos en la primera ronda
for ronda in (1, 2):
respuesta = requests.get(
f"{BASE_URL}/api/get_devices_latest",
headers={"user-api-hash": API_HASH},
params={"time": str(desde)},
timeout=30,
)
respuesta.raise_for_status()
datos = respuesta.json()
print(f"Ronda {ronda}: {len(datos['items'])} vehículos con posición nueva")
for vehiculo in datos["items"]:
print(f" {vehiculo['name']}: {vehiculo['lat']}, {vehiculo['lng']}")
desde = datos["time"] # la próxima ronda pide solo lo que cambió desde aquí
time.sleep(INTERVALO_SEGUNDOS)
```
PHP:
```php
(string) $desde]));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
$datos = json_decode($cuerpo, true);
echo "Ronda $ronda: " . count($datos["items"]) . " vehículos con posición nueva\n";
foreach ($datos["items"] as $vehiculo) {
echo " {$vehiculo["name"]}: {$vehiculo["lat"]}, {$vehiculo["lng"]}\n";
}
$desde = $datos["time"]; // la próxima ronda pide solo lo que cambió desde aquí
sleep($intervaloSegundos);
}
```
## El vehículo más cercano a un punto
La API no calcula distancias entre un punto y tu flota. Para asignar la unidad más cercana a una tarea, toma `lat` y `lng` de cada vehículo desde esta consulta y calcula la distancia en tu sistema (por ejemplo, con la fórmula de Haversine). Verifica antes `online` y `time` para no asignar un vehículo sin conexión.
## Qué no sabemos
La latencia entre que un equipo reporta y la plataforma lo expone en la API no se midió, así que no la publicamos. Valídala con tu propia flota antes de depender de ella para decisiones en tiempo real.
---
> Esta página en HTML: https://trackerintl.com/devs/guias/historial-de-recorridos
> Estado: Parcial, revisada el 2026-10-05. Usa operaciones verificadas por diff contra producción. Los ejemplos se ejecutaron contra un servidor simulado. El rango máximo de 31 días es el valor por defecto del código, no una medición de producción.
# Historial de recorridos
Consulta el recorrido de un vehículo por periodos de hasta 31 días, con tramos de conducción y parada o posiciones GPS crudas paginadas.
## Antes de empezar
- Necesitas el `id` del vehículo: sale de `get_devices`.
- Las fechas y horas se interpretan en la **zona horaria de la cuenta**, no en UTC.
- `get_history` acepta un rango máximo de 31 días y responde `422` si lo superas. Para periodos más largos, divide en tramos de hasta 31 días y consulta uno por uno.
## Recorrido por tramos
**Recorrido de un vehículo por tramos**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_history" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "device_id=9001" \
--data-urlencode "from_date=2026-10-01" \
--data-urlencode "from_time=00:00" \
--data-urlencode "to_date=2026-10-02" \
--data-urlencode "to_time=23:59"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
device_id: "9001",
from_date: "2026-10-01",
from_time: "00:00",
to_date: "2026-10-02",
to_time: "23:59",
});
const respuesta = await fetch(`${BASE_URL}/api/get_history?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_history",
headers={"user-api-hash": API_HASH},
params={"device_id": "9001", "from_date": "2026-10-01", "from_time": "00:00", "to_date": "2026-10-02", "to_time": "23:59"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"9001", "from_date" => "2026-10-01", "from_time" => "00:00", "to_date" => "2026-10-02", "to_time" => "23:59"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
Cada elemento de `items` es un tramo de conducción o de parada, según su `status`. Los totales (`distance_sum`, `top_speed`…) vienen con unidad, como texto.
## Posiciones crudas paginadas
Si necesitas cada posición GPS, usa `get_history_messages`. Avanza con `page` hasta llegar a `last_page`, y recuerda que `limit` solo admite 50, 100, 200, 300, 400, 500 o 1000.
**Posiciones GPS crudas paginadas**
cURL:
```bash
curl -sS -G "https://plataforma.trackerintl.com/api/get_history_messages" \
-H "user-api-hash: TU_API_HASH" \
--data-urlencode "device_id=9001" \
--data-urlencode "from_date=2026-10-01" \
--data-urlencode "from_time=00:00" \
--data-urlencode "to_date=2026-10-02" \
--data-urlencode "to_time=23:59" \
--data-urlencode "limit=100" \
--data-urlencode "page=1"
```
JavaScript:
```javascript
const BASE_URL = "https://plataforma.trackerintl.com";
const API_HASH = "TU_API_HASH";
const params = new URLSearchParams({
device_id: "9001",
from_date: "2026-10-01",
from_time: "00:00",
to_date: "2026-10-02",
to_time: "23:59",
limit: "100",
page: "1",
});
const respuesta = await fetch(`${BASE_URL}/api/get_history_messages?${params}`, {
headers: { "user-api-hash": API_HASH },
});
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}: ${await respuesta.text()}`);
console.log(JSON.stringify(await respuesta.json(), null, 2));
```
Python:
```python
import json
import requests
BASE_URL = "https://plataforma.trackerintl.com"
API_HASH = "TU_API_HASH"
respuesta = requests.get(
f"{BASE_URL}/api/get_history_messages",
headers={"user-api-hash": API_HASH},
params={"device_id": "9001", "from_date": "2026-10-01", "from_time": "00:00", "to_date": "2026-10-02", "to_time": "23:59", "limit": "100", "page": "1"},
timeout=30,
)
respuesta.raise_for_status()
print(json.dumps(respuesta.json(), indent=2, ensure_ascii=False))
```
PHP:
```php
"9001", "from_date" => "2026-10-01", "from_time" => "00:00", "to_date" => "2026-10-02", "to_time" => "23:59", "limit" => "100", "page" => "1"]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["user-api-hash: " . $apiHash],
]);
$cuerpo = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($cuerpo === false || $codigo !== 200) {
fwrite(STDERR, "HTTP $codigo\n");
exit(1);
}
echo json_encode(json_decode($cuerpo, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";
```
## Recomendaciones
- Pide solo el rango que necesitas: las respuestas de historial pueden ser grandes.
- Para análisis largos, guarda lo que descargas en tu propio almacén en lugar de volver a pedirlo.
- Si el vehículo está vencido, la API responde `422`. Mira [Errores](/devs/errores).
---
> Esta página en HTML: https://trackerintl.com/devs/mcp
> Estado: Parcial, revisada el 2026-10-05. Probado únicamente contra un servidor simulado con datos ficticios (26 pruebas automáticas). No se ha usado contra una cuenta real de producción y el paquete todavía no se distribuye públicamente.
# Servidor MCP
Servidor MCP de solo lectura para que un asistente de IA consulte tu cuenta de la plataforma GPS: herramientas, instalación, seguridad y límites.
El servidor MCP de Tracker International permite que un asistente de IA compatible con el [Model Context Protocol](https://modelcontextprotocol.io) **consulte** tu cuenta de la plataforma GPS: vehículos, posiciones, historial, eventos, geocercas y reportes. Es de **solo lectura**: no tiene herramientas que modifiquen datos, envíen comandos, generen reportes ni administren clientes.
> **Estado y disponibilidad.**
>
> Hoy es un paquete en borrador y **no existe un servidor MCP público alojado por Tracker International**. Está pensado para distribuirse como un paquete que ejecutas tú, con tu propia cuenta y tu propio hash, en tu equipo o en tu infraestructura. La distribución del paquete y un alojamiento oficial están en evaluación. Si quieres probarlo, [escríbenos](/contacto).
## Requisitos
- Node.js 22 o superior.
- Un `user_api_hash` de tu cuenta. El servidor nunca pide ni procesa contraseñas. Mira [Autenticación](/devs/autenticacion).
- Recomendado: un usuario dedicado con permisos únicamente de "ver", porque el hash concede los mismos permisos que la contraseña.
## Instalación
Con el paquete en tu equipo:
```bash
cd mcp-server
npm install
npm test
npm run demo
```
`npm test` ejecuta 26 pruebas sin red, contra un servidor simulado. `npm run demo` hace una llamada por herramienta, por stdio y por HTTP, también contra el simulado.
## Configuración
Variables de entorno principales:
| Variable | Valor por defecto | Para qué sirve |
|---|---|---|
| `TRACKER_API_HASH` | (ninguno) | Credencial para el transporte stdio. En HTTP llega por cabecera. |
| `TRACKER_API_BASE_URL` | `https://plataforma.trackerintl.com` | Dominio base. Debe ser https (http solo en localhost). |
| `TRACKER_RATE_LIMIT_PER_MINUTE` | 60 | Peticiones por minuto y por credencial hacia la plataforma. |
| `TRACKER_MAX_RANGE_DAYS` | 31 | Rango máximo de fechas en historial, posiciones y eventos. |
| `TRACKER_MAX_OUTPUT_CHARS` | 60000 | Tope de la respuesta de cada herramienta. |
| `TRACKER_LANG` | (ninguno) | Idioma de los textos traducibles (`es`, `en`). |
## Conectarlo a un cliente
### Claude Desktop (stdio)
En `claude_desktop_config.json`:
```json
{
"mcpServers": {
"tracker-gps": {
"command": "node",
"args": ["/ruta/absoluta/mcp-server/src/stdio.js"],
"env": {
"TRACKER_API_HASH": "TU_API_HASH",
"TRACKER_API_BASE_URL": "https://plataforma.trackerintl.com"
}
}
}
}
```
### Claude Code (stdio)
```bash
claude mcp add tracker-gps --env TRACKER_API_HASH='TU_API_HASH' -- node /ruta/absoluta/mcp-server/src/stdio.js
```
### Clientes HTTP
El transporte HTTP escucha por defecto en `127.0.0.1:3333` (endpoint `POST /mcp`) y no tiene sesiones. La credencial va **en cada petición**, en la cabecera `X-Tracker-Api-Hash` (o `Authorization: Bearer`). El servidor no termina TLS: si lo expones fuera de tu equipo, ponlo detrás de un proxy HTTPS y restringe el acceso por red.
## Herramientas
Todas son de solo lectura (`readOnlyHint`). Por defecto devuelven proyecciones compactas; las que lo permiten aceptan `raw: true` para la respuesta cruda.
| Herramienta | Qué hace | Endpoint de la API |
|---|---|---|
| `get_account` | Datos de la cuenta | `GET /api/get_user_data` |
| `list_vehicles` | Listar vehículos | `GET /api/get_devices` |
| `get_latest_positions` | Últimas posiciones / cambios recientes | `GET /api/get_devices_latest` |
| `get_route_history` | Historial de recorrido por tramos | `GET /api/get_history` |
| `get_position_messages` | Posiciones GPS crudas (paginadas) | `GET /api/get_history_messages` |
| `list_events` | Eventos y alertas disparadas | `GET /api/get_events` |
| `list_alerts` | Reglas de alerta configuradas | `GET /api/get_alerts` |
| `get_alerts_summary` | Resumen de eventos por tipo | `GET /api/get_alerts_summary` |
| `list_geofences` | Geocercas | `GET /api/get_geofences` |
| `point_in_geofences` | Geocercas que contienen un punto | `GET /api/point_in_geofences` |
| `get_vehicle_geofences` | Geocercas donde está un vehículo ahora | `GET /api/devices_in_geofences` |
| `list_routes` | Rutas planificadas | `GET /api/get_routes` |
| `list_report_types` | Tipos de reporte disponibles | `GET /api/get_reports_types` |
| `list_saved_reports` | Reportes guardados | `GET /api/get_reports` |
| `list_drivers` | Conductores | `GET /api/get_user_drivers` |
| `list_sent_commands` | Comandos enviados a los equipos | `GET /api/sent_commands` |
La lista sale del manifiesto del servidor en cada compilación de este portal, no se escribe a mano.
## Seguridad
- **Solo lectura por construcción**: el cliente HTTP del servidor solo expone `GET`. Aun así, el hash que le des sigue pudiendo escribir si alguien lo usa fuera de este servidor; por eso conviene un usuario con permisos solo de "ver".
- **La credencial** se lee del entorno (stdio) o de la cabecera de cada petición (HTTP), no se guarda en disco y viaja hacia la plataforma solo en la cabecera `user-api-hash`, nunca en la URL.
- **Registros**: solo incluyen el nombre de la herramienta, el código y la duración, sin argumentos, cuerpos ni credenciales.
- **HTTP**: rechaza `Origin` no autorizado, limita el tamaño del cuerpo y escucha solo en loopback por defecto.
- Se rechazan las redirecciones, para no reenviar la cabecera a otro host.
## Límites
- 60 peticiones por minuto y por credencial desde el servidor (configurable), con una espera máxima de 10 segundos.
- Rango máximo de 31 días en historial, posiciones y eventos.
- `limit` de 200 como máximo; en posiciones crudas, solo 50, 100, 200, 300, 400, 500 o 1000.
- Salida máxima de 60.000 caracteres por herramienta.
- Los límites de la plataforma aparecen en [Límites](/devs/limites).
## Qué no está verificado
No se hizo ninguna petición autenticada a producción desde este servidor. Las formas de respuesta se midieron ejecutando el código de la plataforma en un entorno de pruebas con datos ficticios y se contrastaron con producción por diff. Tampoco se verificó la respuesta real del inicio de sesión ni de las consultas de geocercas por punto o por vehículo.
---
> Esta página en HTML: https://trackerintl.com/devs/ia
> Estado: Verificado, revisada el 2026-10-08. Verificado por las pruebas automáticas del portal, que comprueban que cada página tiene su gemelo en Markdown y que los índices para IA coinciden con las páginas publicadas.
# Usar esta documentación con IA
Cómo leer y citar la documentación de la API con asistentes de IA: Markdown de cada página, llms.txt, texto completo y OpenAPI.
Cada página de este portal está pensada para que una persona **y** una herramienta de IA puedan leerla sin ruido.
## Qué puedes usar
| Recurso | Dirección | Para qué sirve |
|---|---|---|
| Página en Markdown | La misma dirección de la página más `.md`, por ejemplo [/devs/autenticacion.md](/devs/autenticacion.md) | Leer una página sin navegación ni estilos |
| Índice | [/devs/llms.txt](/devs/llms.txt) | Lista de páginas con su descripción y su estado de verificación |
| Texto completo | [/devs/llms-full.txt](/devs/llms-full.txt) | Todas las páginas indexables en un solo archivo |
| OpenAPI | [/devs/openapi.json](/devs/openapi.json) | Referencia máquina-legible, solo con operaciones verificadas |
| Servidor MCP | [Servidor MCP](/devs/mcp) | Consultar tu propia cuenta (solo lectura) desde un asistente |
También puedes copiar el código de cualquier ejemplo con el botón **Copiar**, y buscar con `Ctrl+K` (o `Cmd+K`).
## Cómo citar
- Cita la dirección de la página y la **fecha de revisión** que muestra su estado.
- Respeta el estado: una página **Parcial** o **Pendiente** no debe presentarse como definitiva.
- Los datos de los ejemplos son ficticios. No son datos de ningún cliente.
## Una advertencia para agentes
Esta documentación solo describe operaciones de lectura. Si un agente necesita escribir datos o enviar comandos, esas operaciones **no están documentadas** aquí: no las deduzcas ni las inventes.
---
> Esta página en HTML: https://trackerintl.com/devs/changelog
> Estado: Verificado, revisada el 2026-10-08. Cada entrada corresponde a un cambio realizado en esta documentación. Los cambios de la plataforma completa están en la página de versiones.
# Changelog
Cambios en la documentación y en la superficie de la API publicada en este portal, por fecha. Los cambios de la plataforma están en Versiones.
Los cambios de la **plataforma** (funciones, mejoras, correcciones) están en [Versiones y novedades](/versiones). Esta página registra los cambios de **este portal** y de la API publicada en él.
## 8 de octubre de 2026
- Se publica el portal de desarrolladores en `/devs`, con referencia por grupos, guías, página del servidor MCP, OpenAPI público y archivos `.md`, `llms.txt` y `llms-full.txt`.
- El portal anterior, que estaba en `/developers`, se reemplaza. Sus direcciones antiguas redirigen de forma permanente a su equivalente nuevo.
- Se retiran afirmaciones que no estaban respaldadas: webhooks con eventos concretos, integraciones con pasarelas de pago e ingesta de sensores, y creación de geocercas por API. Webhooks queda como [Pendiente](/devs/referencia/webhooks).
## 5 de octubre de 2026
- Se verifica el catálogo de la API de lectura (OpenAPI 1.0.0) contra producción por lectura del código y diff.
- En producción, las respuestas de vehículos incluyen los campos `no_position_update` y `last_position_time`, que indican si el equipo está conectado pero su última posición válida es antigua.
---
> Esta página en HTML: https://trackerintl.com/devs/seguridad
> Estado: Parcial, revisada el 2026-10-05. Las recomendaciones salen de cómo funciona la credencial, verificado por lectura del código. La política formal de reporte de vulnerabilidades y de rotación de credenciales todavía no está definida.
# Seguridad
Cómo proteger tu user_api_hash y tus integraciones: usuario dedicado, cabecera en lugar de URL, datos de ejemplo ficticios y cómo reportar un problema.
## Tu credencial
- El `user_api_hash` concede **los mismos permisos que la contraseña** del usuario. No hay claves de solo lectura ni por alcance.
- **Crea un usuario dedicado** para cada integración, con permisos únicamente de "ver". Si la integración se compromete, desactivas ese usuario sin afectar a otros.
- Guarda el hash en un gestor de secretos o en variables de entorno. No lo pongas en el código, en repositorios, en tickets ni en aplicaciones que corren en el navegador de tus usuarios.
- **No lo envíes en la URL.** Usa la cabecera `user-api-hash`: las URL acaban en registros de acceso.
- Si cambias la contraseña del usuario, el hash se regenera y el anterior deja de funcionar.
## Datos de ejemplo
Todos los ejemplos de este portal usan marcadores y datos ficticios (`TU_API_HASH`, `demo@example.com`, IMEI y placas de prueba). Ninguna respuesta de ejemplo contiene datos de clientes.
## Qué no se publica
Este portal documenta solo operaciones de lectura verificadas. No se documentan operaciones de escritura, de administración de clientes ni de la aplicación móvil de administración, ni detalles de la infraestructura. Es una decisión de seguridad y de honestidad: solo publicamos lo que podemos respaldar.
## Reportar un problema
Si encuentras un error en la documentación o un problema de seguridad, escribe a [info@trackerintl.com](mailto:info@trackerintl.com). No incluyas credenciales reales en el mensaje.
Consulta también la página general de [seguridad y protección de datos](/seguridad) de Tracker International.
---
> Esta página en HTML: https://trackerintl.com/devs/estado-de-la-documentacion
> Estado: Verificado, revisada el 2026-10-08. La matriz se genera en cada compilación a partir del contenido y del OpenAPI público, así que no puede desfasarse de las páginas.
# Estado de la documentación
Qué páginas y operaciones del portal están Verificadas, Parciales o Pendientes, con la fecha de revisión y cómo se verificó cada una.
Cada página y cada operación de este portal declara su estado. La regla del sitio es publicar solo lo que se puede respaldar, y decir con claridad qué falta.
## Qué significa cada estado
| Estado | Significado |
|---|---|
| Verificado | La ruta existe en producción y el código que la atiende coincide con el verificado. La forma de la respuesta se midió con datos ficticios. |
| Parcial | Una parte se leyó del código y no se midió, o los ejemplos solo se ejecutaron contra un servidor simulado. La página dice qué falta. |
| Pendiente | No hay verificación. La página no contiene detalles técnicos y no se indexa en buscadores. |
## Cómo se verificó
- **Producción**: se confirmó cada ruta con la lista de rutas de la plataforma y se compararon por huella y por diff los archivos del flujo de cada operación.
- **Entorno de pruebas**: se ejecutó el código de la plataforma con datos ficticios, dentro de una transacción que se revirtió, y se midieron códigos HTTP y formas de respuesta.
- **Sin cuenta real**: no se ha hecho ninguna petición autenticada a producción. Por eso ningún estado dice "probado con una cuenta real".
- **Ejemplos**: se ejecutan en cada compilación contra un servidor simulado, y las páginas se revisan automáticamente para que no citen operaciones inexistentes.
## Páginas
| Página | Sección | Estado | Revisada |
|---|---|---|---|
| [Developers](https://trackerintl.com/devs) | Empezar | Parcial | 2026-10-05 |
| [Primeros pasos](https://trackerintl.com/devs/primeros-pasos) | Empezar | Parcial | 2026-10-05 |
| [Autenticación](https://trackerintl.com/devs/autenticacion) | Empezar | Parcial | 2026-10-05 |
| [Convenciones](https://trackerintl.com/devs/convenciones) | Conceptos | Verificado | 2026-10-05 |
| [Errores](https://trackerintl.com/devs/errores) | Conceptos | Verificado | 2026-10-05 |
| [Límites](https://trackerintl.com/devs/limites) | Conceptos | Parcial | 2026-10-05 |
| [Referencia de la API](https://trackerintl.com/devs/referencia) | Referencia de la API | Verificado | 2026-10-05 |
| [Dispositivos](https://trackerintl.com/devs/referencia/dispositivos) | Referencia de la API | Verificado | 2026-10-05 |
| [Historial y rutas](https://trackerintl.com/devs/referencia/historial-y-rutas) | Referencia de la API | Verificado | 2026-10-05 |
| [Eventos y alertas](https://trackerintl.com/devs/referencia/eventos-y-alertas) | Referencia de la API | Verificado | 2026-10-05 |
| [Geocercas](https://trackerintl.com/devs/referencia/geocercas) | Referencia de la API | Parcial | 2026-10-05 |
| [Reportes](https://trackerintl.com/devs/referencia/reportes) | Referencia de la API | Verificado | 2026-10-05 |
| [Usuario](https://trackerintl.com/devs/referencia/usuario) | Referencia de la API | Verificado | 2026-10-05 |
| [Webhooks](https://trackerintl.com/devs/referencia/webhooks) | Referencia de la API | Pendiente | — |
| [Guías](https://trackerintl.com/devs/guias) | Guías | Parcial | 2026-10-05 |
| [Sincronizar con un ERP o CRM](https://trackerintl.com/devs/guias/erp-y-crm) | Guías | Parcial | 2026-10-05 |
| [Posición en vivo](https://trackerintl.com/devs/guias/posicion-en-vivo) | Guías | Parcial | 2026-10-05 |
| [Historial de recorridos](https://trackerintl.com/devs/guias/historial-de-recorridos) | Guías | Parcial | 2026-10-05 |
| [Servidor MCP](https://trackerintl.com/devs/mcp) | IA y herramientas | Parcial | 2026-10-05 |
| [Usar esta documentación con IA](https://trackerintl.com/devs/ia) | IA y herramientas | Verificado | 2026-10-08 |
| [Changelog](https://trackerintl.com/devs/changelog) | Proyecto | Verificado | 2026-10-08 |
| [Seguridad](https://trackerintl.com/devs/seguridad) | Proyecto | Parcial | 2026-10-05 |
| [Estado de la documentación](https://trackerintl.com/devs/estado-de-la-documentacion) | Proyecto | Verificado | 2026-10-08 |
## Operaciones
| Operación | Grupo | Estado | Revisado | Qué falta |
|---|---|---|---|---|
| `POST /api/login` | Autenticación | Parcial | 2026-10-05 | Ver verificación de la operación. |
| `GET /api/get_user_data` | Usuario | Verificado | 2026-10-05 | — |
| `GET /api/get_devices` | Dispositivos | Verificado | 2026-10-05 | Ver verificación de la operación. |
| `GET /api/get_devices_latest` | Dispositivos | Verificado | 2026-10-05 | Ver verificación de la operación. |
| `GET /api/get_history` | Historial y rutas | Verificado | 2026-10-05 | — |
| `GET /api/get_history_messages` | Historial y rutas | Verificado | 2026-10-05 | — |
| `GET /api/get_events` | Eventos y alertas | Verificado | 2026-10-05 | — |
| `GET /api/get_alerts` | Eventos y alertas | Verificado | 2026-10-05 | — |
| `GET /api/get_alerts_summary` | Eventos y alertas | Verificado | 2026-10-05 | — |
| `GET /api/get_geofences` | Geocercas | Verificado | 2026-10-05 | — |
| `GET /api/point_in_geofences` | Geocercas | Parcial | 2026-10-05 | Ver verificación de la operación. |
| `GET /api/devices_in_geofences` | Geocercas | Parcial | 2026-10-05 | Ver verificación de la operación. |
| `GET /api/get_routes` | Geocercas | Parcial | 2026-10-05 | Ver verificación de la operación. |
| `GET /api/get_reports` | Reportes | Verificado | 2026-10-05 | — |
| `GET /api/get_reports_types` | Reportes | Verificado | 2026-10-05 | — |
| `GET /api/get_user_drivers` | Usuario | Verificado | 2026-10-05 | — |
| `GET /api/sent_commands` | Usuario | Verificado | 2026-10-05 | — |
## Qué falta
- Una cuenta de pruebas de solo lectura para verificar las respuestas contra producción y pasar de Parcial a Verificado.
- Confirmar los valores de límites y de caducidad en producción.
- Webhooks, operaciones de escritura, SDK y colección Postman: sin documentar.
- Confirmar el dominio base de la API para todas las cuentas, incluidas las de marca blanca.