La API de EarthRanger permite el acceso programático directo a datos de seguimiento, observaciones y eventos, lo que le permite crear integraciones personalizadas, canales de automatización o paneles externos.
Para Empezar
Explore la API de EarthRanger a través de las siguientes herramientas y documentación oficiales:
-
Documentación interactiva
- Reemplace
<sandbox>en la URL con el nombre de su servidor para interactuar con la API directamente: https://sandbox.pamdas.org/api/v1.0/docs/interactive/
- Reemplace
-
Biblioteca de Python.
- Explora nuestra biblioteca de Python disponible en GitHub. Encuéntrala aquí: https://github.com/PADAS/er-client
-
Documentación completa de la API REST
- Una vez que haya iniciado sesión en el portal de administración de EarthRanger, podrá acceder a la API REST completa, documentada aquí: https://sandbox.pamdas.org/api/v1.0/docs/index.html
-
Ejemplos
- Explora este artículo y /docs/examples
Autenticación
EarthRanger utiliza tokens OAuth2 para el acceso a la API.
- Puedes crear un token de larga duración a través de tu portal de administración
- o solicitar uno al equipo de soporte de EarthRanger.
Visita Crear y administrar tokens de autenticación para integraciones seguras para aprender cómo crear un token de autenticación en el portal de administración.
Eventos
Los eventos representan incidentes, actividades u observaciones en el campo, como avistamientos de vida silvestre, incidentes de seguridad o actividades de patrullaje.
Lista de eventos
/api/v1.0/activity/events/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
page_size Optional
|
integer | Número de resultados por página (predeterminado: 25, máximo: 4000) |
state Optional
|
string | Filtrar por estado: nuevo, activo, resuelto
|
event_type Optional
|
UUID | Filtrar por ID de tipo de evento |
bbox Optional
|
string | Cuadro delimitador: oeste, sur, este, norte
|
sort_by Optional
|
string |
Los valores válidos son El valor predeterminado es '-sort_at', un valor especial que representa el orden inverso de updated_at. |
include_updates Optional
|
boolean | Incluir historial de actualizaciones de eventos |
include_notes Optional
|
boolean | Incluir notas del evento |
include_files Optional
|
boolean | Incluir archivos adjuntos |
Ejemplos de código:
cURL
Solicitud básica:
curl --location 'https://your-domain.pamdas.org/api/v1.0/activity/events/ \
--header 'Authorization: Bearer YOUR_TOKEN_HERE' \
--header 'Accept: application/json'
Con filtros:
curl --location 'https://your-domain.pamdas.org/api/v1.0/activity/events/?state=new&page_size=3&sort_by=-updated_at' \
--header 'Authorization: Bearer YOUR_TOKEN_HERE' \
--header 'Accept: application/json'
Con rango de fechas y cuadro delimitador:
curl -X GET "https://your-domain.pamdas.org/api/v1.0/activity/events/?bbox=34.5,-2.5,34.6,-2.3&since=2024-10-01T00:00:00Z&until=2024-10-19T23:59:59Z" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Accept: application/json"
Python
Usando el cliente Python oficial de EarthRanger (er-client):
Instalar con: pip install earthranger-client
from erclient.client import ERClient
# Initialize client with token (recommended)
client = ERClient(
service_root='https://your-domain.pamdas.org/api/v1.0',
token='YOUR_TOKEN_HERE'
)
# OR initialize with username/password
client = ERClient(
service_root='https://your-domain.pamdas.org/api/v1.0',
username='your_username',
password='your_password',
token_url='https://your-domain.pamdas.org/oauth2/token',
client_id='your_client_id'
)
# Basic request - get_events() returns a generator
# It automatically handles pagination and fetches all events
# Note: This may take time if you have many events
for event in client.get_events():
print(f"Event: {event.get('title', 'No title')} - {event['event_type']}")
print(f"Location: {event.get('location')}")
print(f"Time: {event['time']}")
print("---")
# TIP: Use max_results to limit the number of events fetched
for event in client.get_events(max_results=100):
print(event['title'])
# With filters - supported parameters:
# state, page_size, page, event_type, filter, include_notes,
# include_related_events, include_files, include_details,
# updated_since, include_updates, max_results, oldest_update_date, event_ids
events_list = []
for event in client.get_events(
state='active',
page_size=50,
include_notes=True,
include_files=True
):
events_list.append(event)
print(f"Found {len(events_list)} active events")
# Limit results with max_results
count = 0
for event in client.get_events(state='active', max_results=10):
count += 1
print(f"{count}. {event['title']}")
# Filter by event type
for event in client.get_events(event_type='wildlife_sighting_rep'):
print(f"Wildlife sighting: {event['title']}")
Uso del cliente asíncrono (AsyncERClient) para un mejor rendimiento:
Nota: Requiere Python 3.7+ con soporte asyncio
from erclient.client import AsyncERClient
import asyncio # Built-in Python module
async def list_events():
# Use as async context manager (recommended)
async with AsyncERClient(
service_root='https://your-domain.pamdas.org/api/v1.0',
token='YOUR_TOKEN_HERE'
) as client:
# get_events() returns an async generator
count = 0
async for event in client.get_events(
page_size=100,
filter='{"state":"active"}' # JSON string filter
):
print(f"{event['title']} - {event['state']}")
count += 1
print(f"Total events processed: {count}")
# Run the async function
asyncio.run(list_events())
Usando la biblioteca de solicitudes directamente (sin er-client):
import requests
API_URL = 'https://your-domain.pamdas.org/api/v1.0'
TOKEN = 'YOUR_TOKEN_HERE'
headers = {
'Authorization': f'Bearer {TOKEN}',
'Accept': 'application/json'
}
# Make request
params = {
'state': 'active',
'page_size': 50,
'sort_by': '-updated_at'
}
response = requests.get(
f'{API_URL}/activity/events/',
headers=headers,
params=params
)
if response.status_code == 200:
data = response.json()
print(f"Total count: {data['count']}")
for event in data['results']:
print(f"{event['title']} - {event['state']}")
# Handle pagination
while data.get('next'):
response = requests.get(data['next'], headers=headers)
data = response.json()
for event in data['results']:
print(f"{event['title']} - {event['state']}")
else:
print(f"Error: {response.status_code}")
print(response.text)
Node.js
Usando axios:
const axios = require('axios');
const API_URL = 'https://your-domain.pamdas.org/api/v1.0';
const TOKEN = 'YOUR_TOKEN_HERE';
async function listEvents() {
try {
const response = await axios.get(
`${API_URL}/activity/events/`,
{
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Accept': 'application/json'
},
params: {
state: 'active',
page_size: 50,
sort_by: '-updated_at'
}
}
);
console.log(`Total events: ${response.data.count}`);
response.data.results.forEach(event => {
console.log(`${event.title || 'Untitled'} - ${event.state}`);
if (event.location) {
console.log(`Location: ${event.location.latitude}, ${event.location.longitude}`);
}
});
// Check if there are more pages
if (response.data.next) {
console.log('More results available at:', response.data.next);
}
return response.data;
} catch (error) {
console.error('Error fetching events:', error.response?.data || error.message);
throw error;
}
}
// Run the function
listEvents();
Usando la búsqueda nativa (Node.js 18+):
const API_URL = 'https://your-domain.pamdas.org/api/v1.0';
const TOKEN = 'YOUR_TOKEN_HERE';
async function listEvents() {
const params = new URLSearchParams({
state: 'active',
page_size: '50',
sort_by: '-updated_at'
});
const response = await fetch(
`${API_URL}/activity/events/?${params}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Accept': 'application/json'
}
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(`Found ${data.count} events`);
// Process results
data.results.forEach(event => {
console.log(`${event.title} - ${event.state}`);
});
return data;
}
listEvents()
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Ejemplo de respuesta:
{
"count": 150,
"next": "https://your-domain.pamdas.org/api/v1.0/activity/events/?page=2&state=active&page_size=50",
"previous": null,
"results": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"serial_number": 12345,
"event_type": "wildlife_sighting_rep",
"priority": 200,
"state": "active",
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"time": "2024-10-19T14:30:00Z",
"end_time": null,
"created_at": "2024-10-19T14:35:00Z",
"updated_at": "2024-10-19T14:35:00Z",
"title": "Elephant Sighting",
"reported_by": {
"id": "user123",
"username": "ranger.john",
"first_name": "John",
"last_name": "Doe"
},
"event_details": {
"species": "African Elephant",
"number_of_individuals": 5,
"behavior": "Feeding"
}
},
{
"id": "b2c3d4e5-f6g7-8901-bcde-f12345678901",
"serial_number": 12346,
"event_type": "security_incident",
"priority": 500,
"state": "active",
"location": {
"latitude": -2.456789,
"longitude": 34.678901
},
"time": "2024-10-19T13:15:00Z",
"end_time": null,
"created_at": "2024-10-19T13:20:00Z",
"updated_at": "2024-10-19T13:20:00Z",
"title": "Suspicious Activity Near Boundary",
"reported_by": {
"id": "user456",
"username": "ranger.jane",
"first_name": "Jane",
"last_name": "Smith"
},
"event_details": {
"incident_type": "trespassing",
"number_of_individuals": 3,
"description": "Three individuals spotted near north fence"
}
}
]
}Obtener evento único
/api/v1.0/activity/event/{id}/
Pedido:
GET /api/v1.0/activity/event/a1b2c3d4-e5f6-7890-abcd-ef1234567890/
Authorization: Bearer your_token_here
Respuesta (200 OK):
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"serial_number": 12345,
"event_type": "wildlife_sighting_rep",
"priority": 200,
"state": "active",
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"time": "2024-10-19T14:30:00Z",
"end_time": null,
"created_at": "2024-10-19T14:35:00Z",
"updated_at": "2024-10-19T14:35:00Z",
"title": "Elephant Sighting",
"reported_by": {
"id": "user123",
"username": "ranger.john",
"first_name": "John",
"last_name": "Doe"
},
"event_details": {
"species": "African Elephant",
"number_of_individuals": 5,
"behavior": "Feeding"
},
"notes": [],
"files": [],
"event_category": "monitoring"
}
Crear un evento
/api/v1.0/activity/events/
Para crear un nuevo evento en EarthRanger, envíe una solicitud POST con los detalles del evento.
Campos obligatorios:
| Campo | Tipo | Descripción |
|---|---|---|
event_type |
string | Valor del tipo de evento (debe coincidir con los tipos de eventos configurados) |
time |
datetime | Hora de ocurrencia del evento (formato ISO8601) |
location |
object | Ubicación del evento con latitud y longitud |
Campos opcionales:
| Campo | Tipo | Descripción |
|---|---|---|
priority |
integer | Prioridad del evento (0-500, valor predeterminado: 0) |
state |
string | Estado del evento: nuevo, activo o resuelto |
title |
string | Título/resumen del evento |
event_details |
object | Datos adicionales específicos del evento |
Pedido:
POST /api/v1.0/activity/events/
Authorization: Bearer your_token_here
Content-Type: application/json
{
"event_type": "mist_rep",
"time": "2024-10-19T06:18:44.056439Z",
"priority": 100,
"location": {
"latitude": 47.123,
"longitude": -122.123
},
"title": "Medical Incident - Ranger Station",
"event_details": {
"mistrep_Method": "Air Evac",
"mistrep_Injury": "Malaria",
"mistrep_Symptoms": "Fever and sweating",
"mistrep_Treatment": "Anti malarial medicine"
}
}
Respuesta (201 creados):
{
"id": "f1e2d3c4-b5a6-7890-cdef-123456789abc",
"serial_number": 12346,
"event_type": "mist_rep",
"priority": 100,
"state": "new",
"location": {
"latitude": 47.123,
"longitude": -122.123
},
"time": "2024-10-19T06:18:44.056439Z",
"created_at": "2024-10-19T15:20:00Z",
"updated_at": "2024-10-19T15:20:00Z",
"title": "Medical Incident - Ranger Station",
"reported_by": {
"id": "current_user_id",
"username": "api.user"
},
"event_details": {
"mistrep_Method": "Air Evac",
"mistrep_Injury": "Malaria",
"mistrep_Symptoms": "Fever and sweating",
"mistrep_Treatment": "Anti malarial medicine"
}
}
El event_type y cada clave en event_details deben coincidir con un campo definido en el esquema de tipo de evento configurado (consulte Admin > Activity > Event Types).
Actualizar un evento
/api/v1.0/activity/event/{id}/
Pedido:
PATCH /api/v1.0/activity/event/f1e2d3c4-b5a6-7890-cdef-123456789abc/
Authorization: Bearer your_token_here
Content-Type: application/json
{
"state": "resolved",
"priority": 50
}
Respuesta (200 OK):
{
"id": "f1e2d3c4-b5a6-7890-cdef-123456789abc",
"serial_number": 12346,
"event_type": "mist_rep",
"priority": 50,
"state": "resolved",
"location": {
"latitude": 47.123,
"longitude": -122.123
},
"time": "2024-10-19T06:18:44.056439Z",
"updated_at": "2024-10-19T16:00:00Z",
"title": "Medical Incident - Ranger Station"
}
Lista de tipos de eventos
/api/v2.0/activity/eventtypes/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
include_inactive Optional
|
boolean | Incluir tipos de eventos inactivos en los resultados |
Ejemplos de código:
cURL
curl -X GET "https://your-domain.pamdas.org/api/v2.0/activity/eventtypes/" \\
-H "Authorization: Bearer YOUR_TOKEN_HERE" \\
-H "Accept: application/json"Python
import requests
API_URL = 'https://your-domain.pamdas.org/api/v2.0'
TOKEN = 'YOUR_TOKEN_HERE'
headers = {
'Authorization': f'Bearer {TOKEN}',
'Accept': 'application/json'
}
response = requests.get(f'{API_URL}/activity/eventtypes/', headers=headers)
if response.status_code == 200:
data = response.json()
for event_type in data['data']:
print(f"{event_type['display']} ({event_type['value']})")
print(f" Category: {event_type['category']}")
print(f" Default Priority: {event_type['default_priority']}")
else:
print(f"Error: {response.status_code}")
print(response.text)Node.js
const axios = require('axios');
const API_URL = 'https://your-domain.pamdas.org/api/v2.0';
const TOKEN = 'YOUR_TOKEN_HERE';
async function listEventTypes() {
try {
const response = await axios.get(
\`\${API_URL}/activity/eventtypes/\`,
{
headers: {
'Authorization': \`Bearer \${TOKEN}\`,
'Accept': 'application/json'
}
}
);
console.log('Event Types:');
response.data.data.forEach(eventType => {
console.log(\`\${eventType.display} (\${eventType.value})\`);
console.log(\` Category: \${eventType.category}\`);
});
return response.data;
} catch (error) {
console.error('Error:', error.response?.data || error.message);
throw error;
}
}
listEventTypes();Ejemplo de respuesta:
{
"data": [
{
"id": "8e2812d8-4ac9-4874-b1b2-1adc5ea192b5",
"url": "https://your-domain.pamdas.org/api/v2.0/activity/eventtypes/snare_rep/",
"has_events_assigned": true,
"icon_id": "snare_rep",
"value": "snare_rep",
"display": "Snare",
"ordernum": 160,
"category": "security",
"geometry_type": "Polygon",
"default_priority": 0,
"default_state": "new",
"resolve_time": null,
"auto_resolve": false,
"is_collection": false,
"is_active": true,
"icon": "snare_rep"
}
]
}Obtener un solo tipo de evento
/api/v2.0/activity/eventtypes/{eventtype_value}/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
include_inactive Optional
|
boolean | Incluir tipos de eventos inactivos |
include_schema Optional
|
boolean | Incluir la definición completa del esquema JSON |
Pedido:
GET /api/v2.0/activity/eventtypes/snare_rep/?include_schema=true
Authorization: Bearer your_token_here
Respuesta (200 OK):
{
"data": {
"id": "8e2812d8-4ac9-4874-b1b2-1adc5ea192b5",
"value": "snare_rep",
"display": "Snare",
"category": "security",
"default_priority": 0,
"default_state": "new",
"schema": {
"json": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"number_of_snares_found": {
"type": "number",
"title": "Number of Snares Found"
},
"status": {
"type": "string",
"title": "Status"
},
"comments": {
"type": "string",
"title": "Comments",
"default": ""
}
}
},
"ui": {
"fields": {
"number_of_snares_found": {
"type": "NUMERIC",
"parent": "section-1"
},
"status": {
"type": "CHOICE_LIST",
"inputType": "DROPDOWN",
"parent": "section-1"
}
}
}
}
}
}
Crear tipo de evento
/api/v2.0/activity/eventtypes/
Cree un nuevo tipo de evento con validación de esquema JSON y configuración de UI.
Campos obligatorios:
| Campo | Tipo | Descripción |
|---|---|---|
value |
string | Identificador único para el tipo de evento |
display |
string | Nombre para mostrar |
schema |
string | Definición del esquema JSON (JSON convertido en cadena) |
Campos opcionales:
| Campo | Tipo | Descripción |
|---|---|---|
category |
string | Categoría del evento (por ejemplo, seguridad, monitoreo, logística) |
default_priority |
integer | Prioridad predeterminada (0-500) |
default_state |
string | Estado predeterminado (nuevo, activo, resuelto) |
geometry_type |
string | Punto, cadena de líneas o polígono |
icon |
string | Identificador de icono |
auto_resolve |
boolean | Resolución automática después de resolve_time |
resolve_time |
integer | Tiempo de resolución automática en segundos |
is_active |
boolean | Si el tipo de evento está activo |
Pedido:
POST /api/v2.0/activity/eventtypes/
Authorization: Bearer your_token_here
Content-Type: application/json
{
"value": "custom_incident",
"display": "Custom Incident",
"category": "security",
"default_priority": 300,
"default_state": "new",
"geometry_type": "Point",
"is_active": true,
"schema": "{\\"json\\":{\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"description\\":{\\"type\\":\\"string\\",\\"title\\":\\"Description\\"}}}}"
}
Respuesta (201 creados):
{
"data": {
"resource_url": "/api/v2.0/activity/eventtypes/custom_incident/"
}
}
Actualizar tipo de evento
/api/v2.0/activity/eventtypes/{eventtype_value}/
Pedido:
PATCH /api/v2.0/activity/eventtypes/custom_incident/
Authorization: Bearer your_token_here
Content-Type: application/json
{
"default_priority": 400,
"is_active": false
}
Respuesta (200 OK):
{
"data": {
"id": "uuid-here",
"value": "custom_incident",
"display": "Custom Incident",
"default_priority": 400,
"is_active": false
}
}
Eliminar tipo de evento
/api/v2.0/activity/eventtypes/{eventtype_value}/
Pedido:
DELETE /api/v2.0/activity/eventtypes/custom_incident/
Authorization: Bearer your_token_here
Códigos de respuesta:
-
204 No Content: Eliminado correctamente -
404 Not Found: No se encontró el tipo de evento -
409 Conflict: El tipo de evento tiene eventos asociados y no se puede eliminar
Nota: Los tipos de evento con eventos asociados no se pueden eliminar. Debe eliminar o reasignar todos los eventos antes de eliminarlos.
Publicación de datos del sensor
El concepto general es que un proveedor de fuentes es un servicio que describe una o más fuentes que proporcionan información de ubicación de un sujeto.
Por ejemplo, un fabricante de dispositivos de rastreo es un proveedor de fuentes, un rastreador de rinocerontes es una fuente y el propio rinoceronte es el sujeto.
Como es habitual, los encabezados de API:
Authorization: Bearer <token>
Accept: application/json
Content-Disposition: attachment; filename={}
Content-Type: application/jsonSujetos
Los sujetos representan entidades rastreadas, como vida silvestre, guardabosques, vehículos o cualquier activo monitoreado.
Lista de temas
/api/v1.0/subjects/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
subject_group Optional
|
UUID | Filtrar por grupo temático |
tracks Optional
|
boolean | Incluir datos de seguimiento recientes |
tracks_since Optional
|
datetime | Hora de inicio de las pistas (ISO8601) |
bbox Optional
|
string | Filtrar por cuadro delimitador |
subject_type Optional
|
string | Filtrar por tipo de tema |
Pedido:
GET /api/v1.0/subjects/?tracks=true&tracks_since=2024-10-01T00:00:00Z
Authorization: Bearer your_token_here
Respuesta (200 OK):
[
{
"id": "abc12345-def6-7890-ghij-klmnopqrstuv",
"name": "Elephant_001",
"subject_type": "wildlife",
"subject_subtype": "elephant",
"is_active": true,
"region": "North Sector",
"sex": "male",
"additional": {
"age_years": 15,
"collar_id": "COL-001"
},
"last_position": {
"id": "pos123",
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"recorded_at": "2024-10-19T14:00:00Z"
},
"tracks": [
{
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"recorded_at": "2024-10-19T14:00:00Z"
},
{
"location": {
"latitude": -2.345123,
"longitude": 34.568123
},
"recorded_at": "2024-10-19T13:00:00Z"
}
]
}
]
Obtener un solo sujeto
/api/v1.0/subject/{id}/
Pedido:
GET /api/v1.0/subject/abc12345-def6-7890-ghij-klmnopqrstuv/
Authorization: Bearer your_token_here
Respuesta (200 OK):
{
"id": "abc12345-def6-7890-ghij-klmnopqrstuv",
"name": "Elephant_001",
"subject_type": "wildlife",
"subject_subtype": "elephant",
"is_active": true,
"region": "North Sector",
"sex": "male",
"additional": {
"age_years": 15,
"collar_id": "COL-001"
},
"last_position": {
"id": "pos123",
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"recorded_at": "2024-10-19T14:00:00Z"
}
}
Crear un sujeto
/api/v1.0/subjects/
Cree un nuevo sujeto en EarthRanger. Los sujetos suelen crearse automáticamente al publicar observaciones, pero también pueden crearse manualmente.
Campos obligatorios:
| Campo | Tipo | Descripción |
|---|---|---|
name |
string | Nombre del sujeto (debe ser único) |
subject_subtype |
UUID or string | Identificador de subtipo de sujeto |
Pedido:
POST /api/v1.0/subjects/
Authorization: Bearer your_token_here
Content-Type: application/json
{
"name": "Ranger_Vehicle_05",
"subject_subtype": "4x4",
"additional": {
"plate_number": "GNP-005",
"model": "Land Cruiser"
}
}
Respuesta (201 creados):
{
"id": "xyz98765-abc4-3210-defg-hijklmnopqrs",
"name": "Ranger_Vehicle_05",
"subject_type": "vehicle",
"subject_subtype": "4x4",
"is_active": true,
"additional": {
"plate_number": "GNP-005",
"model": "Land Cruiser"
},
"created_at": "2024-10-19T15:30:00Z"
}
Observaciones
Las observaciones son puntos de datos de seguimiento/ubicación individuales recopilados de dispositivos o fuentes.
Lista de observaciones
/api/v1.0/observations/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
subject_id Optional
|
UUID | Filtrar por tema |
source_id Optional
|
UUID | Filtrar por fuente |
since Optional
|
datetime | Fecha/hora de inicio (ISO8601) |
until Optional
|
datetime | Fecha/hora de finalización (ISO8601) |
Pedido:
GET /api/v1.0/observations/?subject_id=abc123&since=2024-10-01T00:00:00Z
Authorization: Bearer your_token_here
Respuesta (200 OK):
[
{
"id": "obs-12345",
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"recorded_at": "2024-10-19T14:00:00Z",
"created_at": "2024-10-19T14:05:00Z",
"source": "gps-collar-001",
"subject": {
"id": "abc123",
"name": "Elephant_001"
},
"additional": {
"speed": 2.5,
"heading": 145
}
}
]
Crear una observación
/api/v1.0/observations/
Publica un punto de seguimiento o la lectura de un sensor. Si el sistema no ha detectado la combinación source-provider/manufacturer_id anteriormente, creará fuentes y sujetos automáticamente.
Campos obligatorios:
| Campo | Tipo | Descripción |
|---|---|---|
location |
object | Objeto de ubicación con latitud y longitud |
recorded_at |
datetime | Hora en que se registró la observación (ISO8601) |
Campos opcionales:
| Campo | Tipo | Descripción |
|---|---|---|
manufacturer_id |
string | Identificador único del dispositivo |
source_type |
string | Tipo de fuente (p. ej., dispositivo_de_seguimiento) |
subject_name |
string | Nombre del sujeto que se está rastreando |
subject_type |
string | Tipo de sujeto (por ejemplo, vida silvestre, persona, vehículo) |
subject_subtype |
string | Subtipo de sujeto (p. ej., elefante, guardabosques, automóvil) |
additional |
object | Metadatos adicionales (velocidad, rumbo, etc.) |
Pedido:
POST /api/v1.0/observations/
Authorization: Bearer your_token_here
Content-Type: application/json
{
"location": {
"lat": 47.123,
"lon": -122.123
},
"recorded_at": "2024-10-19T13:59:15.000Z",
"manufacturer_id": "COLLAR-12345",
"subject_name": "Elephant_001",
"subject_type": "wildlife",
"subject_subtype": "elephant",
"source_type": "tracking_device",
"additional": {
"speed": 2.5,
"heading": 145,
"battery_level": 85
}
}
Respuesta (201 creados):
{
"id": "obs-98765",
"location": {
"latitude": 47.123,
"longitude": -122.123
},
"recorded_at": "2024-10-19T13:59:15.000Z",
"created_at": "2024-10-19T14:00:00Z",
"source": {
"id": "src-12345",
"manufacturer_id": "COLLAR-12345",
"source_type": "tracking_device"
},
"subject": {
"id": "subj-67890",
"name": "Elephant_001",
"subject_type": "wildlife",
"subject_subtype": "elephant"
},
"additional": {
"speed": 2.5,
"heading": 145,
"battery_level": 85
}
}
Nota: Si pasa una observación en la que el sistema no ha visto antes esa combinación source-provider/manufacturer_id, creará fuentes y sujetos según sea necesario.
Grupos de sujetos
Los grupos de temas organizan los temas en categorías jerárquicas para su gestión y filtrado.
Lista de grupos de temas
/api/v1.0/subjectgroups/
Pedido:
GET /api/v1.0/subjectgroups/
Authorization: Bearer your_token_here
Respuesta (200 OK):
[
{
"id": "grp-12345",
"name": "Wildlife",
"subject_count": 45,
"is_visible": true,
"children": [
{
"id": "grp-12346",
"name": "Elephants",
"subject_count": 12,
"is_visible": true
},
{
"id": "grp-12347",
"name": "Rhinos",
"subject_count": 8,
"is_visible": true
}
]
},
{
"id": "grp-12348",
"name": "Rangers",
"subject_count": 25,
"is_visible": true
}
]
Lista de sujetos en el grupo
/api/v1.0/subjectgroup/{id}/subjects/
Pedido:
GET /api/v1.0/subjectgroup/grp-12346/subjects/
Authorization: Bearer your_token_here
Respuesta (200 OK):
[
{
"id": "subj-001",
"name": "Elephant_001",
"subject_type": "wildlife",
"subject_subtype": "elephant",
"is_active": true,
"last_position": {
"latitude": -2.345678,
"longitude": 34.567890,
"recorded_at": "2024-10-19T14:00:00Z"
}
},
{
"id": "subj-002",
"name": "Elephant_002",
"subject_type": "wildlife",
"subject_subtype": "elephant",
"is_active": true,
"last_position": {
"latitude": -2.346789,
"longitude": 34.568901,
"recorded_at": "2024-10-19T14:05:00Z"
}
}
]
Patrullas
Las patrullas representan operaciones de campo con segmentos, puntos de referencia y eventos asociados.
Lista de patrullas
/api/v1.0/activity/patrols/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
exclude_empty_patrols Optional
|
boolean | Excluir patrullas sin segmentos ni eventos |
patrol_type Optional
|
UUID | Filtrar por tipo de patrulla |
since Optional
|
datetime | Fecha/hora de inicio (ISO8601) |
until Optional
|
datetime | Fecha/hora de finalización (ISO8601) |
Pedido:
GET /api/v1.0/activity/patrols/?exclude_empty_patrols=true
Authorization: Bearer your_token_here
Respuesta (200 OK):
{
"count": 10,
"results": [
{
"id": "patrol-12345",
"serial_number": 501,
"title": "Morning Patrol - North Sector",
"patrol_type": "routine",
"state": "done",
"start_time": "2024-10-19T06:00:00Z",
"end_time": "2024-10-19T12:00:00Z",
"patrol_segments": [
{
"id": "seg-001",
"scheduled_start": "2024-10-19T06:00:00Z",
"scheduled_end": "2024-10-19T08:00:00Z",
"time_range": {
"start_time": "2024-10-19T06:15:00Z",
"end_time": "2024-10-19T07:45:00Z"
},
"leader": {
"id": "user-123",
"name": "John Ranger"
}
}
],
"events": [
{
"id": "evt-456",
"event_type": "wildlife_sighting_rep",
"time": "2024-10-19T07:30:00Z"
}
]
}
]
}
Obtener una patrulla única
/api/v1.0/activity/patrol/{id}/
Pedido:
GET /api/v1.0/activity/patrol/patrol-12345/
Authorization: Bearer your_token_here
Respuesta (200 OK):
{
"id": "patrol-12345",
"serial_number": 501,
"title": "Morning Patrol - North Sector",
"patrol_type": "routine",
"state": "done",
"start_time": "2024-10-19T06:00:00Z",
"end_time": "2024-10-19T12:00:00Z",
"created_at": "2024-10-18T20:00:00Z",
"updated_at": "2024-10-19T12:05:00Z",
"patrol_segments": [
{
"id": "seg-001",
"scheduled_start": "2024-10-19T06:00:00Z",
"scheduled_end": "2024-10-19T08:00:00Z",
"time_range": {
"start_time": "2024-10-19T06:15:00Z",
"end_time": "2024-10-19T07:45:00Z"
},
"leader": {
"id": "user-123",
"username": "john.ranger",
"name": "John Ranger"
},
"events": [
{
"id": "evt-456",
"event_type": "wildlife_sighting_rep",
"title": "Elephant Sighting",
"time": "2024-10-19T07:30:00Z"
}
]
}
],
"notes": []
}
Seguimiento de sujetos
Los datos de seguimiento muestran el historial de movimiento de un sujeto a lo largo del tiempo.
Obtener seguimiento de sujetos
/api/v1.0/subject/{subject_id}/tracks/
Parámetros de consulta:
| Parametro | Tipo | Descripción |
|---|---|---|
since Optional
|
datetime | Fecha/hora de inicio (ISO8601) |
until Optional
|
datetime | Fecha/hora de finalización (ISO8601) |
format Optional
|
string | Formato de respuesta: json, geojson o csv |
Pedido:
GET /api/v1.0/subject/abc123/tracks/?since=2024-10-01T00:00:00Z
Authorization: Bearer your_token_here
Respuesta (200 OK):
[
{
"id": "track-001",
"location": {
"latitude": -2.345678,
"longitude": 34.567890
},
"recorded_at": "2024-10-19T14:00:00Z",
"created_at": "2024-10-19T14:05:00Z",
"source": "gps-collar-001",
"additional": {
"speed": 2.5,
"heading": 145,
"altitude": 1250
}
},
{
"id": "track-002",
"location": {
"latitude": -2.345123,
"longitude": 34.568123
},
"recorded_at": "2024-10-19T13:00:00Z",
"created_at": "2024-10-19T13:05:00Z",
"source": "gps-collar-001",
"additional": {
"speed": 1.8,
"heading": 150,
"altitude": 1245
}
}
]
Patrones comunes
Formato de fecha y hora
Todos los parámetros de fecha y hora deben estar en formato ISO8601 con zona horaria:
2024-10-19T14:30:00Z # UTC
2024-10-19T14:30:00-06:00 # With timezone offset
Paginación
La mayoría de los puntos finales de lista admiten la paginación con los parámetros page y page_size. La respuesta incluye los campos count, next y previous.
Solicitud Página 1:
GET /api/v1.0/activity/events/?page_size=25
Respuesta:
{
"count": 150,
"next": "https://your-domain.pamdas.org/api/v1.0/activity/events/?page=2&page_size=25",
"previous": null,
"results": [...]
}
Solicitar página siguiente:
GET /api/v1.0/activity/events/?page=2&page_size=25
Filtrado por cuadro delimitador
Formato: oeste, sur, este, norte (longitud_mínima, latitud_mínima, longitud_máxima, latitud_máxima)
?bbox=-122.5,48.4,-122.0,49.0
Obtenga todos los eventos dentro de un área geográfica:
GET /api/v1.0/activity/events/?bbox=34.5,-2.5,34.6,-2.3
Authorization: Bearer your_token_here
Esto devuelve todos los eventos con ubicaciones entre:
- Longitud: 34,5° a 34,6° Este
- Latitud: -2,5° a -2,3° Sur
Manejo de errores
La API devuelve códigos de estado HTTP estándar. Respuestas de error comunes:
| Código de estado | Significado | Causas Comunes |
|---|---|---|
400 |
Solicitud incorrecta | Parámetros no válidos o JSON mal formado |
401 |
No autorizado | Token de autenticación faltante o no válido |
403 |
Prohibido | Permisos insuficientes |
404 |
No encontrado | El recurso no existe |
500 |
Error del servidor | Error Interno del Servidor |
400 Solicitud incorrecta:
{
"status": {
"code": 400,
"message": "Bad Request",
"details": "Invalid datetime format for 'since' parameter"
}
}
401 No autorizado:
{
"detail": "Authentication credentials were not provided."
}
