Patrol Sync¶
Recommendations for synchronizing patrol-related data for offline use. Aimed at mobile clients that want to minimize downloads and the number of checks to detect changes.
This guide covers patrol types (the catalog of types, like “Foot”, “Vehicle”), the tracked-by schema (who can be reported as segment leader), and patrols and patrol segments (the actual patrol instances and their segments). Only patrol types and the tracked-by schema support conditional GET (ETag / Last-Modified); patrol and segment list/detail do not.
Note: The web UI currently supports only one patrol segment per patrol. The API allows multiple segments per patrol, but clients that aim for parity with the web UI should treat one segment per patrol as the supported case.
Patrol Types (catalog for offline)¶
Patrol types define the kinds of patrols (e.g. Foot, Vehicle). Sync the list for offline so the app can create patrols and show type metadata (display name, icon, priority, etc.).
Recommended: List with conditional GET¶
Use the patrol types list as the single source for the catalog. The response ETag and Last-Modified reflect any change to the list (metadata only: value, display, ordernum, icon_id, is_active, default_priority, updated_at, image_url). Use If-None-Match and optionally If-Modified-Since to avoid re-downloading when nothing has changed.
Endpoint
GET /api/v1.0/activity/patrols/types
Query parameters
There are no query parameters for the list; it returns all patrol types the user is allowed to see.
Optimizing “do I need to download?”
Store the ETag (and optionally Last-Modified) from the last successful list response.
On the next sync, send a conditional request with the same URL and:
If-None-Match: your stored ETag value
Optionally If-Modified-Since: your stored Last-Modified value (RFC 7232 format).
If the server responds 304 Not Modified, nothing changed — skip downloading the body and keep your cached patrol types.
If the server responds 200 OK, replace your cache with the new response and store the new ETag and Last-Modified.
This minimizes both checks (one conditional GET) and downloads (no body when unchanged).
Optimizing full download
Initial / full sync: Call the list once; there is no
include_schemaorupdated_sincefor patrol types — the list is small and metadata-only. One request is enough.
Single patrol type (detail)¶
GET /api/v1.0/activity/patrols/types/<patrol_type_id>
Use the patrol type UUID. Supports conditional GET (ETag and Last-Modified). Use when you need to refresh or check a single type; for full catalog sync, the list endpoint with conditional GET is simpler.
Tracked-by schema (segment leaders / “reported by”)¶
When creating or editing patrol segments, clients often need the list of subjects (and optionally users) that can be selected as segment leader (“tracked by”). This is provided by the tracked-by schema endpoint. It has an ETag so you can avoid re-downloading when the set of possible leaders has not changed.
Endpoint
GET /api/v1.0/activity/patrols/trackedby
Returns metadata describing the schema for “tracked by” (e.g. subject list). The ETag is derived from the set of subjects available to the user. Use If-None-Match for conditional GET; 304 Not Modified means the schema/options are unchanged.
Optimizing “do I need to download?”
Store the ETag from the last successful response.
On the next sync, send If-None-Match: your stored ETag value.
304 — keep your cached schema; 200 — replace cache and store the new ETag.
Patrols (list and detail)¶
Patrols are the actual patrol instances (with one or more segments). The list and detail endpoints do not support ETag or Last-Modified. Use query parameters to limit results and paginate; for incremental-style sync you can use the filter with a date_range to fetch patrols overlapping a time window.
Endpoints
GET /api/v1.0/activity/patrols
GET /api/v1.0/activity/patrols/<patrol_id>
List query parameters
Parameter |
Description |
|---|---|
|
JSON object for advanced filtering. Example: |
|
If |
|
Filter by state(s). Allowed values depend on |
Responses are paginated. There is no ETag on the list or detail; you cannot use conditional GET to skip re-downloading. To minimize data transfer, use filter (e.g. date_range) and pagination, and store the last sync time or last requested range for incremental pulls.
Single patrol
Detail returns one patrol by UUID. No ETag; refetch when you need the latest state.
Patrol request payloads (POST, PATCH, PUT)¶
Create patrol: POST /api/v1.0/activity/patrols
Update patrol: PATCH or PUT /api/v1.0/activity/patrols/<patrol_id>
Request body (JSON). All fields are optional on create; omit fields you are not changing on update.
Field |
Type |
Description |
|---|---|---|
|
string |
Patrol objective (text). Can be blank/null. |
|
integer |
Priority. Allowed values: |
|
string |
Patrol state: |
|
string |
Patrol title. Max 255 characters. Can be blank/null. |
|
array |
List of note objects. Each note: |
|
array |
List of segment objects (see Patrol segment request payloads below). When creating a patrol you can include segments in this array; do not send |
Validation: scheduled_start must be earlier than scheduled_end in any segment. Patrol state may be auto-set to "done" when all segments have ended.
Patrol segments (list and detail)¶
Patrol segments are the legs of a patrol (each has a type, time range, leader, etc.). The web UI supports only one segment per patrol; the API may return or accept multiple segments per patrol, but the web UI does not. List and detail do not support ETag or Last-Modified.
Endpoints
GET /api/v1.0/activity/patrols/segments
GET /api/v1.0/activity/patrols/segments/<segment_id>
List is paginated. Use when syncing segment data; no conditional GET is available.
Segment–events
GET /api/v1.0/activity/patrols/segments/<patrol_segment_id>/events
Returns events linked to a patrol segment. No ETag.
Patrol segment request payloads (POST, PATCH, PUT)¶
Segments can be created or updated in two ways: nested in a patrol (in patrol_segments when posting to POST /patrols or PATCH/PUT /patrols/<id>) or standalone via the segments list.
Create segment (standalone): POST /api/v1.0/activity/patrols/segments
Update segment (standalone): PATCH or PUT /api/v1.0/activity/patrols/segments/<segment_id>
When nested in a patrol payload, omit patrol (the server sets it). When posting to the segments endpoint, patrol is required (patrol UUID).
Field |
Type |
Required (standalone) |
Description |
|---|---|---|---|
|
UUID |
Yes (standalone only) |
Patrol ID. Omit when segment is nested in a patrol body. |
|
string |
No |
Patrol type |
|
object |
No |
Segment leader (who is tracked). Object: |
|
string |
No |
ISO 8601 date/time. Scheduled start. |
|
string |
No |
ISO 8601 date/time. Scheduled end. Must be after |
|
object |
No |
Actual patrol time. |
|
object |
No |
Start point. |
|
object |
No |
End point. Same shape as |
|
UUID |
No |
Include when updating an existing segment (nested or standalone). |
Read-only in responses (do not send when creating/updating): image_url, icon_id, events.
Patrol sub-resources (notes, files)¶
Patrol notes and files are nested under a patrol:
GET /api/v1.0/activity/patrols/<patrol_id>/notes
GET /api/v1.0/activity/patrols/<patrol_id>/notes/<note_id>
GET /api/v1.0/activity/patrols/<patrol_id>/files
GET /api/v1.0/activity/patrols/<patrol_id>/files/...
These endpoints do not expose ETag or Last-Modified. Refetch as needed.
Note request payloads: Create note with POST .../patrols/<patrol_id>/notes with body { "text": "..." }. Update with PATCH/PUT .../notes/<note_id>. The patrol is implied by the URL.
File request payloads: Create/update patrol files via the files endpoint; the payload typically includes usercontent_id, usercontent_type, and optionally comment, ordernum. See the API or schema for the full file upload format.
Summary for mobile clients¶
Goal |
Recommendation |
|---|---|
Fewest “has anything changed?” checks |
Use conditional GET only where supported: patrol types list and tracked-by schema. Send If-None-Match (and optionally If-Modified-Since for patrol types). |
Patrol types catalog |
GET …/patrols/types with If-None-Match; treat 304 as “no download needed”. One list call is enough for full catalog; no |
Tracked-by / segment leaders |
GET …/patrols/trackedby with If-None-Match; 304 means schema/options unchanged. |
Patrols list |
No ETag. Use filter (e.g. date_range with |
Patrol and segment detail |
No ETag. Refetch when you need the latest state. |
Where conditional GET works |
Patrol types (list and single) and tracked-by schema only. Patrols and segments do not support ETag/Last-Modified. |