Autenticación
La API de GeoTareas usa un modelo de autenticación dual para proteger cada endpoint protegido. Cada solicitud debe incluir tanto un token JWT (que representa la identidad y los permisos del usuario) como una clave de API (que representa la integración de tu empresa). Este enfoque en capas garantiza que tanto el usuario como la integración se validen de forma independiente en cada llamada.
Flujo de autenticación
El siguiente diagrama ilustra el flujo de autenticación de extremo a extremo:
- Autenticarse — Llamá a
POST /apidev/v1/logincon tu email y contraseña. - Recibir un JWT — El servidor devuelve un token firmado que expira 1 hora después de su emisión, por defecto.
- Adjuntar credenciales — Incluí el JWT, tu clave de API y el encabezado
tenanten cada solicitud posterior. - Manejar la expiración — Cuando recibas un
401, descartá el token y volvé a autenticarte. No hay un flujo de token de actualización.
Cada solicitud — incluido el inicio de sesión — debe incluir el encabezado tenant. El tenant por defecto es geotareas.com; si a tu empresa se le asignó un tenant dedicado, debés enviar ese valor asignado en su lugar. Un tenant ausente o incorrecto se rechaza con 401.
Inicio de sesión
| Método | POST |
| URL | /apidev/v1/login |
Encabezados
| Encabezado | Valor | Requerido |
|---|---|---|
tenant | Tu dominio de tenant asignado (por defecto: geotareas.com) — enviá siempre tu tenant asignado | Sí |
Content-Type | application/json | Sí |
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
email | string | Sí | La dirección de email del usuario. |
password | string | Sí | La contraseña del usuario. |
playerid | string | No | Id de player de notificaciones push para registrar a este usuario (máx 200). |
Ejemplos de código
- cURL
- JavaScript
- Python
- PHP
- C#
curl -X POST https://api.example.com/apidev/v1/login \
-H "tenant: $TENANT" \
-H "Content-Type: application/json" \
-d '{
"email": "dev@company.com",
"password": "your_password"
}'
const TENANT = "geotareas.com";
const response = await fetch("https://api.example.com/apidev/v1/login", {
method: "POST",
headers: {
"tenant": TENANT,
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "dev@company.com",
password: "your_password",
}),
});
const data = await response.json();
const token = data.data.authorization;
import requests
TENANT = "geotareas.com"
response = requests.post(
"https://api.example.com/apidev/v1/login",
headers={
"tenant": TENANT,
"Content-Type": "application/json",
},
json={
"email": "dev@company.com",
"password": "your_password",
},
)
data = response.json()
token = data["data"]["authorization"]
<?php
$tenant = "geotareas.com";
$ch = curl_init("https://api.example.com/apidev/v1/login");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"tenant: {$tenant}",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"email" => "dev@company.com",
"password" => "your_password",
]),
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$token = $data["data"]["authorization"];
using System.Net.Http;
using System.Text;
using System.Text.Json;
var tenant = "geotareas.com";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("tenant", tenant);
var payload = JsonSerializer.Serialize(new
{
email = "dev@company.com",
password = "your_password"
});
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.example.com/apidev/v1/login", content);
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var token = doc.RootElement
.GetProperty("data")
.GetProperty("authorization")
.GetString();
Respuesta exitosa
{
"success": true,
"data": {
"authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"meta": {}
}
Respuestas de error
Credenciales inválidas
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid email or password."
}
}
Encabezado tenant ausente
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "The tenant header is required."
}
}
Token expirado
Un JWT válido que superó su vigencia de 1 hora devuelve TOKEN_EXPIRED (distinto del código UNAUTHORIZED que se devuelve por credenciales incorrectas):
{
"success": false,
"error": {
"code": "TOKEN_EXPIRED",
"message": "The token has expired. Log in again to obtain a new one."
}
}
Compañía suspendida
Si la cuenta de la compañía está suspendida por Logicsat (por ejemplo, por motivos administrativos), la API devuelve 403 COMPANY_SUSPENDED en todos los endpoints, incluido el login — no se emiten tokens nuevos y los tokens emitidos previamente dejan de funcionar de inmediato. Tu integración debería tomarlo como señal de dejar de reintentar y contactar al soporte de Logicsat:
{
"success": false,
"error": {
"code": "COMPANY_SUSPENDED",
"message": "The company account is suspended. Please contact Logicsat support."
}
}
Uso del token
Una vez autenticado, incluí estos tres encabezados en cada solicitud a la API:
| Encabezado | Valor | Descripción |
|---|---|---|
tenant | geotareas.com (por defecto) | Tu namespace de tenant asignado — debe enviarse en cada solicitud. |
Authorization | Bearer eyJhbG... | El JWT obtenido del inicio de sesión. |
X-API-Key | gtk_xxxxxxxxxxxx | La clave de integración de tu empresa. |
Solicitud de ejemplo:
curl -X GET "https://api.example.com/apidev/v1/fleet/devices?limit=25&offset=0" \
-H "tenant: $TENANT" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY"
Ciclo de vida del token
| Propiedad | Detalle |
|---|---|
| Tiempo de vida (TTL) | 1 hora desde la emisión. |
| Mecanismo de actualización | Ninguno. Volvé a autenticarte llamando al endpoint de inicio de sesión cuando el token expire. |
| Señal de expiración | Una respuesta 401 TOKEN_EXPIRED indica que el token expiró; una 401 UNAUTHORIZED indica que es inválido. |
Cuando recibas una respuesta 401, descartá el token actual y hacé un nuevo inicio de sesión para obtener uno nuevo. No hay un flujo de token de actualización -- simplemente volvé a llamar a /apidev/v1/login.
Validación de la clave de API
La clave de API se le entrega a tu organización durante el onboarding. En cada solicitud, el servidor valida las siguientes propiedades de tu clave:
| Verificación | Descripción |
|---|---|
| Coincidencia de tenant | La clave debe pertenecer al mismo tenant que el JWT. |
| Alcance | La clave debe tener un alcance de integración válido asignado durante el onboarding. |
| Estado | La clave debe estar en estado active. |
| Ventana de tiempo | La hora actual debe estar dentro del rango valid_from y valid_until de la clave. |
Si alguna de estas verificaciones falla, el servidor responde con 401 UNAUTHORIZED.
Buenas prácticas de seguridad
- Almacená los tokens de forma segura. Mantené los JWT en memoria o en almacenamiento seguro. Nunca los persistas en el almacenamiento local de clientes web.
- Nunca expongas las claves de API en código del lado del cliente. Las claves de API solo deben usarse en comunicación servidor a servidor o entornos de backend seguros.
- Rotá las claves de API periódicamente. Contactá a tu ejecutivo de cuenta para programar la rotación de claves y minimizar la ventana de exposición.
- Usá siempre HTTPS. Todas las solicitudes a la API deben hacerse sobre TLS. Las solicitudes HTTP sin cifrar serán rechazadas.
Resolución de problemas
| Síntoma | Causa | Solución |
|---|---|---|
401 — "Invalid email or password" | Credenciales incorrectas | Verificá email y contraseña. Las contraseñas distinguen mayúsculas de minúsculas. |
401 — "The tenant header is required" | Encabezado tenant ausente o vacío | Agregá el encabezado tenant con tu dominio asignado. |
401 después de un período de llamadas que funcionaban | JWT expirado (TTL de 1 h) | Descartá el token y volvé a llamar a /apidev/v1/login. |
401 — Clave de API rechazada | Clave expirada, inactiva o de tenant incorrecto | Verificá el estado de la clave y su ventana de validez con tu ejecutivo de cuenta. |
429 — Too Many Requests | Protección contra fuerza bruta activada (5 inicios de sesión fallidos en 30 s) | Esperá 60 segundos antes de reintentar. No reintentes en bucle. |