Pular para o conteúdo

Aree (geofence)

Este conteúdo não está disponível em sua língua ainda.

Un’“area” è una geofence — cerchio, luogo, poligono o traccia — nello scope del proprietario della chiave (sue e della sua organizzazione, stesso perimetro della v2). Un’area di un altro cliente risponde sempre 404, mai 403: la chiave non può nemmeno scoprire che esiste.

Campo Tipo Note
id integer
name string max 100 caratteri
color string #rrggbb; #3b82f6 se omesso alla creazione
type enum circle | polygon | polyline | point
center object | null { lat, lng } — cerchi e luoghi; sui poligoni è il centroide calcolato
radius_m number | null solo circle (minimo 25 m) e point (default 50 m se omesso) — massimo 100000 m
buffer_m number | null solo polyline: distanza massima dal percorso, 5–5000 m, default 50 m
points array { lat, lng }[] — poligoni (≥3) e tracce (≥2), massimo 2000
external_id string | null codice dell’area nel tuo sistema (gestionale, CRM…); unico per account; 1–100 caratteri tra lettere, numeri e . _ : / @ # -
dwell_seconds integer permanenza minima prima di notificare l’entrata, in secondi; 0 = subito (default, comportamento invariato) — vedi Permanenza minima
metrics.area_m2 number | null poligoni e cerchi
metrics.perimeter_m number | null poligoni e cerchi
metrics.length_m number | null solo tracce
bounds object | null { south, west, north, east }
created_at string (ISO 8601) | null

Un intero da 0 a 86400 secondi, pensato per non notificare chi attraversa un’area senza fermarsi (una strada che passa dentro un cantiere, un incrocio dentro il raggio di un cliente). 0 è il default e si comporta esattamente come sempre: l’entrata si notifica appena il dispositivo è dentro.

Con un valore maggiore di zero, l’evento di entrata (notifiche, automazioni, e la voce in GET /areas/{id}/events e in /api/v3/events) scatta solo dopo che il dispositivo è rimasto dentro l’area per almeno quel tempo, misurato sugli orari GPS dei punti ricevuti — non sull’orologio del server. Chi attraversa l’area senza fermarsi altrettanto a lungo non genera né entrata né uscita. Se il dispositivo esce prima che il tempo sia trascorso, il conteggio si azzera: un’entrata successiva riparte da zero. L’uscita, di conseguenza, si notifica solo dopo un’entrata già confermata.

Si imposta con dwell_seconds in POST/PATCH/PUT .../by-external-id e in properties.dwell_seconds su POST /areas/import; nell’editor delle aree dell’app corrisponde al campo “Notifica l’entrata dopo (minuti)”. Fuori range → 422 VALIDATION_ERROR con code: "OUT_OF_RANGE" — vedi Codici di validazione.

Elenco delle aree. Permesso: read_areas.

Parametro Default Note
limit 50 1–200, clampato
offset 0
type — circle | polygon | polyline | point; altri valori → 400 BAD_REQUEST
format json geojson restituisce una FeatureCollection in data
external_id — cerca l’area con questo codice esterno esatto (0 o 1 risultato); non è un filtro parziale
Finestra del terminale
curl -H "X-API-Key: $AITRACK_API_KEY" \
"https://api.aitrack.it/api/v3/areas?type=circle&limit=50"
{
"data": [
{
"id": 42,
"name": "Magazzino Nord",
"color": "#3b82f6",
"type": "circle",
"center": { "lat": 45.4642, "lng": 9.19 },
"radius_m": 150,
"buffer_m": null,
"points": [],
"metrics": { "area_m2": 70686, "perimeter_m": 942, "length_m": null },
"bounds": { "south": 45.4629, "west": 9.1881, "north": 45.4655, "east": 9.1919 },
"created_at": "2026-09-20T10:00:00.000Z"
}
],
"meta": { "pagination": { "limit": 50, "offset": 0, "count": 1 }, "total": 1 }
}

Con ?format=geojson la stessa area esce come Feature dentro una FeatureCollection — vedi GeoJSON: input e output.

Crea un’area. Permesso: write_areas. Header opzionale Idempotency-Key (24 h) — vedi Panoramica. Il corpo accetta la forma JSON v3 oppure { "geojson": <Feature> }.

Finestra del terminale
curl -X POST https://api.aitrack.it/api/v3/areas \
-H "X-API-Key: $AITRACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Magazzino Nord",
"color": "#3b82f6",
"type": "circle",
"center": { "lat": 45.4642, "lng": 9.19 },
"radius_m": 150
}'
{
"data": {
"id": 42,
"name": "Magazzino Nord",
"color": "#3b82f6",
"type": "circle",
"center": { "lat": 45.4642, "lng": 9.19 },
"radius_m": 150,
"buffer_m": null,
"points": [],
"metrics": { "area_m2": 70686, "perimeter_m": 942, "length_m": null },
"bounds": { "south": 45.4629, "west": 9.1881, "north": 45.4655, "east": 9.1919 },
"created_at": "2026-09-27T09:00:00.000Z"
}
}

Risposta 201, header Location: /api/v3/areas/42. Se ci sono avvisi non bloccanti (vertici quasi coincidenti, area molto piccola o molto grande) compare anche meta.warnings. Dati non validi → 422 VALIDATION_ERROR con details[] — vedi Codici di validazione. external_id già usato da un’altra tua area → 409 CONFLICT: usa PUT /api/v3/areas/by-external-id/{external_id} per aggiornarla invece di provare a ricrearla.

Crea l’area se non esiste ancora un’area con questo external_id, altrimenti la aggiorna — upsert. Permesso: write_areas. Pensata per una sincronizzazione che rimanda periodicamente tutto il proprio elenco: chiamata più volte con lo stesso elenco, non crea mai doppioni.

Parametro path Note
externalId 1–100 caratteri tra lettere, numeri e . _ : / @ # -; altrimenti 400 BAD_REQUEST prima di consultare il database

Il corpo è la stessa forma JSON v3 di POST/PATCH. Se il corpo include anche external_id, deve coincidere con quello nel percorso, altrimenti 422 VALIDATION_ERROR (code: "MISMATCH").

Finestra del terminale
# Prima sincronizzazione del cliente 4521: l'area non esiste ancora → creata (201)
curl -X PUT https://api.aitrack.it/api/v3/areas/by-external-id/erp-cliente-4521 \
-H "X-API-Key: $AITRACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Cliente 4521 — Rossi Trasporti",
"type": "circle",
"center": { "lat": 45.4642, "lng": 9.19 },
"radius_m": 150
}'
{
"data": {
"id": 88,
"name": "Cliente 4521 — Rossi Trasporti",
"color": "#3b82f6",
"type": "circle",
"center": { "lat": 45.4642, "lng": 9.19 },
"radius_m": 150,
"buffer_m": null,
"points": [],
"external_id": "erp-cliente-4521",
"dwell_seconds": 0,
"metrics": { "area_m2": 70686, "perimeter_m": 942, "length_m": null },
"bounds": { "south": 45.4629, "west": 9.1881, "north": 45.4655, "east": 9.1919 },
"created_at": "2026-09-27T02:00:00.000Z"
},
"meta": { "upsert": "created" }
}

La notte dopo, la stessa chiamata con lo stesso externalId — per esempio col centro aggiornato, perché il cliente ha cambiato sede — aggiorna l’area 88 invece di crearne una nuova (200, meta.upsert: "updated"); come su PATCH, solo i campi inviati cambiano:

{ "data": { "id": 88, "name": "Cliente 4521 — Rossi Trasporti", "center": { "lat": 45.4650, "lng": 9.1905 } }, "meta": { "upsert": "updated" } }

Risposta 201 (creata, header Location) o 200 (aggiornata) — distinguibile anche da meta.upsert ("created" | "updated"). 400 BAD_REQUEST se l’externalId nel percorso non è valido; 422 VALIDATION_ERROR se i dati non sono validi o se il corpo contiene un external_id diverso da quello nel percorso (MISMATCH).

Dettaglio di un’area. Permesso: read_areas. ?format=geojson restituisce una Feature. Id malformato (non un intero positivo) → 400 BAD_REQUEST, prima di consultare il database. Id valido ma inesistente o di un altro cliente → 404 NOT_FOUND (le due situazioni non si distinguono).

Finestra del terminale
curl -H "X-API-Key: $AITRACK_API_KEY" \
https://api.aitrack.it/api/v3/areas/42

Modifica parziale. Permesso: write_areas. Solo i campi inviati cambiano; quelli omessi restano quelli salvati.

Finestra del terminale
# Solo il nome
curl -X PATCH https://api.aitrack.it/api/v3/areas/42 \
-H "X-API-Key: $AITRACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Magazzino Nord — ingresso camion"}'
Finestra del terminale
# Da cerchio a poligono: va inviata la nuova geometria
curl -X PATCH https://api.aitrack.it/api/v3/areas/42 \
-H "X-API-Key: $AITRACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "polygon",
"points": [
{ "lat": 45.4640, "lng": 9.1895 },
{ "lat": 45.4648, "lng": 9.1900 },
{ "lat": 45.4638, "lng": 9.1912 }
]
}'

external_id e dwell_seconds si modificano come ogni altro campo — vedi Modello e Permanenza minima. Inviare "external_id": null rimuove il codice esterno dall’area (per esempio se smetti di sincronizzarla da un sistema esterno); ometterlo lascia quello già salvato, come per ogni altro campo di una PATCH parziale.

Risposta 200 con l’area completa aggiornata (stessa forma di GET). 400 BAD_REQUEST se l’id nel percorso è malformato (non un intero positivo). 404 NOT_FOUND se non trovata, 422 VALIDATION_ERROR se i dati non sono validi.

Elimina un’area. Permesso: write_areas. Risposta 204 senza corpo. 400 BAD_REQUEST se l’id è malformato. 404 NOT_FOUND se non trovata o non tua.

Finestra del terminale
curl -X DELETE https://api.aitrack.it/api/v3/areas/42 \
-H "X-API-Key: $AITRACK_API_KEY"

Importa più aree da GeoJSON in una chiamata. Permesso: write_areas. Massimo 200 aree per chiamata (le feature oltre la 200ª vengono scartate e segnalate in errors, le prime 200 si processano comunque). Corpo: una FeatureCollection, una Feature o una Geometry nuda, come corpo diretto oppure dentro { "geojson": ... }. Una feature non valida non blocca le altre.

?dry_run=true (o "dry_run": true nel corpo) valida senza salvare nulla: utile per mostrare all’utente cosa importerebbe prima di confermare.

Una feature con properties.external_id aggiorna l’area esistente con quel codice invece di duplicarla (elencata in data.updated, non in data.created) — stessa logica di PUT .../by-external-id, utile per importare un’esportazione GeoJSON già arricchita con i codici del proprio sistema. properties.dwell_seconds imposta la permanenza minima di ogni area importata, con la stessa regola di POST/PATCH diretti. Una geometria Multi* (MultiPoint, MultiLineString, MultiPolygon) con properties.external_id sulla stessa feature produrrebbe più aree con lo stesso codice: viene rifiutata con l’errore MULTI_GEOMETRY_EXTERNAL_ID — dividerla in una feature per parte, ciascuna col proprio external_id, oppure ometterlo.

Finestra del terminale
curl -X POST "https://api.aitrack.it/api/v3/areas/import?dry_run=true" \
-H "X-API-Key: $AITRACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "name": "Cantiere Sud", "radius": 100 },
"geometry": { "type": "Point", "coordinates": [9.195, 45.460] } },
{ "type": "Feature", "properties": { "name": "Deposito" },
"geometry": { "type": "Polygon", "coordinates": [[[9.10,45.40],[9.10,45.40]]] } }
]
}'
{
"data": {
"dry_run": true,
"valid": 1,
"created": [],
"updated": [],
"errors": [{ "index": 1, "code": "errPolygonMin" }],
"warnings": []
}
}

Risposta 200 se dry_run, oppure se la chiamata reale ha solo aggiornato aree esistenti (nessuna creata); 201 se almeno un’area è stata creata; 422 se nessuna era valida. errors[] mescola due famiglie di codici, distinguibili dalla presenza di message:

  • dalla lettura del GeoJSON (solo index e code): errGeoJsonInvalid, errGeometryUnsupported, errCoordinates, errTooManyPoints, errPolygonMin, errPolylineMin, errTooManyFeatures (quest’ultimo con index: -1, quando ci sono più di 200 feature), MULTI_GEOMETRY_EXTERNAL_ID (index, code e message: un external_id su una geometria Multi*).
  • dalla validazione dell’area risultante (index, code, message): gli stessi codici della tabella Codici di validazione qui sotto.

warnings[] (non bloccanti): warnHolesIgnored (i buchi interni di un poligono vengono ignorati), warnSimplifiedDuplicates (vertici quasi coincidenti semplificati).

Il punto lat/lng è dentro l’area? Permesso: read_areas. Stessa regola geometrica del motore che genera gli eventi di ingresso/uscita, quindi risponde in modo coerente con le notifiche che riceve chi ha configurato un avviso su quell’area.

Parametro Obbligatorio
lat sì
lng sì
Finestra del terminale
curl -H "X-API-Key: $AITRACK_API_KEY" \
"https://api.aitrack.it/api/v3/areas/42/contains?lat=45.4642&lng=9.19"
{ "data": { "inside": true, "distance_m": -37.5 } }

distance_m è negativa dentro l’area (metri dal bordo verso l’interno), positiva fuori; può essere null se non calcolabile. lat/lng mancanti o fuori range, oppure id malformato → 400 BAD_REQUEST. Id valido ma di un’area inesistente o di un altro cliente → 404 NOT_FOUND.

Entrate e uscite registrate dalle regole di notifica geofence del proprietario della chiave su quest’area — con una permanenza minima impostata su dwell_seconds, le entrate qui elencate rispettano quella regola: chi ha solo attraversato l’area non compare. Permesso: read_areas.

Parametro Default Note
event — entry | exit — filtra solo ingressi o solo uscite; altro valore → 400 BAD_REQUEST
device — filtro per IMEI
from / to — finestra temporale, ISO 8601; non parsabile → 400 BAD_REQUEST
limit 50 1–200
offset 0
Finestra del terminale
curl -H "X-API-Key: $AITRACK_API_KEY" \
"https://api.aitrack.it/api/v3/areas/42/events?event=entry&limit=50"
{
"data": [
{
"id": 5521,
"area_id": 42,
"device": "352093081452312",
"event": "entry",
"position": { "lat": 45.4641, "lng": 9.1899 },
"timestamp": "2026-09-27T08:59:00.000Z"
}
],
"meta": { "pagination": { "limit": 50, "offset": 0, "count": 1 } }
}

Id malformato → 400 BAD_REQUEST, prima di consultare il database. Id valido ma di un’area inesistente o di un altro cliente → 404 NOT_FOUND.

Ogni voce è un elemento di details[] nella risposta 422 VALIDATION_ERROR di POST/PATCH/ PUT .../by-external-id (creazione, modifica e upsert diretti) — su POST /areas/import gli stessi codici compaiono in errors[].code insieme a quelli della lettura del GeoJSON, vedi sopra. external_id già usato da un’altra area non è un errore di validazione: risponde 409 CONFLICT (senza details[]) solo su POST /areas — vedi creazione e upsert qui sopra.

Campo Codice Quando
name REQUIRED nome mancante o vuoto
name TOO_LONG oltre 100 caratteri
type INVALID mancante o diverso da circle/polygon/polyline/point
color INVALID non nel formato #rrggbb
center REQUIRED manca il centro su un cerchio o un luogo
center INVALID_COORDINATES lat/lng del centro fuori range o non numerici
points INVALID_COORDINATES un punto ha lat/lng fuori range o non numerici
external_id INVALID non rispetta il formato: 1–100 caratteri tra lettere, numeri e . _ : / @ # -
external_id MISMATCH solo su PUT .../by-external-id/{externalId}: il corpo contiene un external_id diverso da quello nel percorso
dwell_seconds OUT_OF_RANGE non è un intero tra 0 e 86400
points TOO_FEW_POINTS meno di 3 punti su un poligono, meno di 2 su una traccia
points TOO_MANY_POINTS oltre 2000 punti
points SELF_INTERSECTION i lati del poligono si incrociano
radius_m OUT_OF_RANGE sotto 25 m (solo cerchio) o sopra 100000 m (cerchio e luogo)
buffer_m OUT_OF_RANGE fuori da 5–5000 m (solo traccia)
geojson INVALID_GEOJSON { "geojson": ... } non contiene una geometria valida
(nessuno) INVALID_BODY il corpo della richiesta non è un oggetto JSON

Per creare o modificare un’area da GeoJSON, il corpo di POST/PATCH /areas/{id} accetta { "geojson": <Feature | Geometry> } — una geometria per chiamata. POST /areas/import accetta invece direttamente una FeatureCollection, una Feature o una Geometry nuda (anche dentro { "geojson": ... }), fino a 200 elementi, con Multi* che diventano un’area per parte:

Geometria GeoJSON Diventa Note
Point point (luogo, raggio 50 m) oppure circle se la feature ha properties.radius
LineString polyline properties.bufferDistance (o buffer_distance) imposta il margine, altrimenti 50 m
Polygon polygon i buchi interni (anelli oltre il primo) sono scartati con l’avviso warnHolesIgnored
MultiPoint / MultiLineString / MultiPolygon più aree solo su /areas/import, un’area per parte

properties.name (o properties.title) e properties.color (o properties.stroke, se #rrggbb) diventano name/color se il corpo della richiesta non li sovrascrive. Stessa regola per properties.external_id e properties.dwell_seconds → external_id/dwell_seconds.

In lettura, ?format=geojson su GET /areas e GET /areas/{id} restituisce una Feature (o una FeatureCollection sull’elenco) RFC 7946:

{
"data": {
"type": "Feature",
"id": 42,
"geometry": { "type": "Point", "coordinates": [9.19, 45.4642] },
"properties": {
"id": 42,
"name": "Magazzino Nord",
"color": "#3b82f6",
"type": "circle",
"radius": 150,
"external_id": null,
"dwell_seconds": 0,
"metrics": { "area_m2": 70686, "perimeter_m": 942, "length_m": null },
"created_at": "2026-09-27T09:00:00.000Z"
}
}
}