Personnel Performance Report
Per-person productivity over a period: tasks, occupancy, worked vs. rested time, kilometers, and GPS-based travel.
GET
/apidev/v1/reports/cpm/personnel-performancePermissionAPICLI_RPTCPM_RENDIMIENTOPERSONAS
Rate Limit10 req/min
Cache300s
Max Range31 days
Overview
Returns one row per person (or per person and day) over the selected period, aggregating tasks handled, time on task, worked and rested hours, kilometers driven, and GPS-derived travel metrics. Use it to measure occupancy, utilization, and workload across your field staff.
- Grouping —
grouping=personrolls up the whole range into one row per person;grouping=person_dayreturns one row per person per day - Live day —
include_today=truemerges the in-progress (not-yet-closed) day as an approximate live estimate - Scope filters — narrow results by person, device, device type, or shift category
- GPS ambiguity — when a vehicle is shared on the same day, GPS-derived fields (circulating, average speed, fuel) are returned as
nullinstead of a misleading value
Request
Request Headers
Every request to a protected endpoint requires these headers:
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token obtained from the Login endpoint. Format: Bearer <token> |
X-API-Key | Yes | Company integration key provided during onboarding. Format: gtk_xxx... |
tenant | Yes | Your assigned tenant domain (default: geotareas.com) — always send your assigned tenant |
Content-Type | Conditional | application/json — required for POST and PUT requests |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
startdate | string | Yes | — | ISO 8601 start date (e.g. 2026-03-01). |
enddate | string | Yes | — | ISO 8601 end date. Max range 31 days from startdate. |
grouping | string | No | person | Aggregation level: person or person_day. |
persons | string | No | All visible | Comma-separated person IDs. Max 500. |
devices | string | No | All visible | Comma-separated device IDs. Max 500. |
device_types | string | No | — | Comma-separated device type IDs. Max 100. |
shift_categories | string | No | — | Comma-separated shift category IDs. Max 50. |
include_today | boolean | No | true | Merge the in-progress day as an approximate live estimate. |
sort_by | string | No | — | Field to sort by (validated against an allow-list). |
sort_dir | string | No | asc | Sort direction: asc or desc. |
limit | integer | No | 25 | Number of records per page (1–100). |
offset | integer | No | 0 | Number of records to skip for pagination. |
Code Examples
- cURL
- JavaScript
- Python
curl -s -H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT" \
"https://$TENANT/apidev/v1/reports/cpm/personnel-performance?startdate=2026-03-01&enddate=2026-03-15&grouping=person&limit=25"
const res = await fetch(
`https://${TENANT}/apidev/v1/reports/cpm/personnel-performance?startdate=2026-03-01&enddate=2026-03-15&grouping=person&limit=25`,
{
headers: {
'Authorization': `Bearer ${token}`,
'X-API-Key': apiKey,
'tenant': TENANT,
},
}
);
const data = await res.json();
import requests
response = requests.get(
f"https://{TENANT}/apidev/v1/reports/cpm/personnel-performance",
headers={"Authorization": f"Bearer {token}", "X-API-Key": api_key, "tenant": TENANT},
params={"startdate": "2026-03-01", "enddate": "2026-03-15", "grouping": "person", "limit": 25},
)
data = response.json()
Response
Response Fields
| Field | Type | Description |
|---|---|---|
person_id | string | Person identifier. |
person_name | string | Person name. |
day | string | null | Day (YYYY-MM-DD); present only when grouping=person_day. |
day_start | string | null | First shift start of the day/range. |
day_end | string | null | Last shift end of the day/range. |
shifts_count | number | Number of shifts. |
vehicles_count | number | Distinct vehicles used (rotation). |
total_tasks | number | Total tasks handled. |
scheduled_tasks | number | Scheduled tasks. |
unscheduled_tasks | number | Unscheduled tasks. |
shift_minutes | number | Gross shift time in minutes. |
worked_minutes | number | Net worked time (shift minus rest). |
task_busy_minutes | number | Net time on task. |
occupancy_pct | number | null | Busy time as a percentage of net worked time (capped at 100). |
shift_kms | number | Kilometers during the shift. |
rest_minutes | number | Total rest time in minutes. |
rests_count | number | Number of rest periods. |
gps_ambiguous | boolean | true when the vehicle was shared; GPS-derived fields are then null. |
circulating_minutes | number | null | Minutes in motion (null if GPS ambiguous). |
trips_total | number | Number of trips. |
trips_kms | number | Kilometers across trips. |
max_speed | number | null | Maximum speed in km/h (null if GPS ambiguous). |
avg_speed | number | null | Average speed in km/h (null if GPS ambiguous). |
estimated_fuel_liters | number | null | Estimated fuel consumption in liters (null if GPS ambiguous). |
utilization_pct | number | null | Circulating time as a percentage of active engine time (null if GPS ambiguous). |
tasks_per_hour | number | null | Tasks completed per hour. |
idle_minutes | number | Idle (dead) time in minutes. |
status | string | null | Shift status: OK, WARN, or ERROR. |
closed | boolean | false when the day is still in progress (live estimate). |
Example Response
{
"success": true,
"data": [
{
"person_id": "982710394857201700",
"person_name": "Carlos Martinez",
"day": null,
"day_start": "2026-03-01T07:00:00",
"day_end": "2026-03-15T15:30:00",
"shifts_count": 11,
"vehicles_count": 2,
"total_tasks": 84,
"scheduled_tasks": 70,
"unscheduled_tasks": 14,
"shift_minutes": 5280,
"worked_minutes": 4960,
"task_busy_minutes": 3820,
"occupancy_pct": 77.0,
"shift_kms": 1620.4,
"rest_minutes": 320,
"rests_count": 18,
"gps_ambiguous": false,
"circulating_minutes": 2740,
"trips_total": 96,
"trips_kms": 1598.2,
"max_speed": 92.0,
"avg_speed": 41.5,
"estimated_fuel_liters": 214.7,
"utilization_pct": 68.0,
"tasks_per_hour": 1.0,
"idle_minutes": 410,
"status": "OK",
"closed": true
},
{
"person_id": "982710394857201701",
"person_name": "Laura Hernandez",
"day": null,
"day_start": "2026-03-02T08:00:00",
"day_end": "2026-03-14T18:00:00",
"shifts_count": 9,
"vehicles_count": 1,
"total_tasks": 61,
"scheduled_tasks": 52,
"unscheduled_tasks": 9,
"shift_minutes": 4320,
"worked_minutes": 4080,
"task_busy_minutes": 2610,
"occupancy_pct": 64.0,
"shift_kms": 980.1,
"rest_minutes": 240,
"rests_count": 11,
"gps_ambiguous": true,
"circulating_minutes": null,
"trips_total": 0,
"trips_kms": 0,
"max_speed": null,
"avg_speed": null,
"estimated_fuel_liters": null,
"utilization_pct": null,
"tasks_per_hour": 0.9,
"idle_minutes": 360,
"status": "WARN",
"closed": true
}
],
"meta": {
"total": 2,
"limit": 25,
"offset": 0
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Invalid parameters (e.g. date range exceeds 31 days). |
UNAUTHORIZED | 401 | Missing, invalid, or expired tenant / Authorization / X-API-Key |
FORBIDDEN | 403 | User lacks required permission. |
RATE_LIMITED | 429 | Exceeded 10 req/min. |
INTERNAL_ERROR | 500 | Unexpected server error. |