HTTP contract
Request
Section titled “Request”| Part | Value |
|---|---|
| Method | http.method: POST (default) or PUT |
| URL | http.url. No URL (null or '') = record only, never upload. |
Content-Type |
application/json |
Accept |
application/json |
| Other headers | http.headers, then Authorization: Bearer <accessToken> when credentials are stored (it wins over a configured Authorization). |
| Timeout | http.timeoutSec (default 30 s, 5–120) |
Each record is the location record JSON, or the rendered
http.locationTemplate.
| Config | Body |
|---|---|
batch: true (default) |
{ ...params, [rootProperty ?? 'locations']: [r1, r2, …] }, up to maxBatchSize records |
batch: false |
{ ...params, [rootProperty ?? 'location']: r }, one request per record |
batch: false, rootProperty: '.' |
{ ...params, ...r } |
batch: true, rootProperty: '.' |
Treated as 'locations' (so a remote config that turns batching on is never refused). |
If a params key has the same name as the root property, the records win. Records go oldest first.
When uploads happen
Section titled “When uploads happen”- A record is inserted and
uploadIntervalSecis0, or that many seconds have passed since the last attempt. - Connectivity comes back.
sync()orsetAuthorization()is called (both skip backoff and the 15-minute auth pause).- The 60-second watchdog finds records waiting and the next-attempt time has passed.
One upload at a time. A run keeps sending until the queue is empty or a request fails. Uploads continue after
stop() until the queue is empty.
Response handling
Section titled “Response handling”| Response | Records | Then |
|---|---|---|
| 2xx | Deleted. | Backoff resets. If the body is JSON with a long_ping object: reset: true clears the server’s stored values, then config is merged into them when remoteConfig is true and configchange fires; stop: true stops tracking and fires enabledchange. |
| 401 / 403 | Kept. | With a stored refreshUrl: POST refreshPayload with refreshHeaders, read access_token | accessToken and optional refresh_token | refreshToken, store them, retry the batch once. Otherwise, or if the refresh fails: authorizationRequired = true, authorization event { status, error }, uploads pause until setAuthorization() or 15 minutes pass. Location keeps running. |
| 413 | Kept. | Retried immediately with half the batch (minimum 1). |
| 408, 429, 5xx, network error, timeout | Kept. | Backoff min(900, 10 × 2ⁿ) s with ±20 % jitter. A Retry-After header in seconds wins (iOS also accepts the HTTP-date form). |
| Other 4xx | attempts + 1; records reaching 3 attempts are deleted (logged as dropped). |
The current run ends; records retry on the next trigger at normal pace. |
Every attempt emits an http event { status, ok, count, responseText } (status 0 = network error,
responseText = the first 2 KB) and writes a log line. While a request is in flight the device holds a partial
wake lock (Android, 60 s timeout) or a background task (iOS).
Remote directives
Section titled “Remote directives”{ "long_ping": { "reset": false, "config": { "movingIntervalSec": 5 }, "stop": false } }| Key | Effect |
|---|---|
long_ping.reset |
true clears every value the server has set; runs before config in the same reply. |
long_ping.config |
Partial config, kept as the server’s own layer on top of the app’s config, so it survives configure() and restarts. Ignored (not stored) when remoteConfig is false. A config that fails validation is rejected whole and logged. |
long_ping.stop |
true stops tracking. Queued records still upload. |
Only read from 2xx responses. See Remote config & remote stop.
Token refresh request
Section titled “Token refresh request”POST <refreshUrl>Content-Type: application/json<refreshHeaders>
{"refresh_token": "<stored refresh token>"}The body is refreshPayload with {{refreshToken}} replaced; the default is shown. The reply must be JSON
with access_token (or accessToken) and may include refresh_token (or refreshToken).