Which API calls the SDK makes
The SDK makes exactly three kinds of request, all on the frames service. It never looks frames up by state, LGA, ward or box.Get your key
Create an API key in your LOCI dashboard under Developers → New key: pick a scope and an expiry, and copy the secret, which is shown once. See Authentication for the full story, and Scopes for what each preset reaches.A key is read-only, acts for one organisation, and expires. Every device
that uses the same key shares one rate-limit budget — see
cost.
Initialise the SDK
CallLoci.initialize once, for example in Application.onCreate. Read the
key from your own secure storage. After that, Loci.frames is the object
every example here uses. Calling it again replaces the key; Loci.dispose()
releases the SDK, and Loci.isInitialized says whether it is ready.
initialise
LociFramesOutcome: Success with the value, or Failure with a
LociFramesError. See Errors and events.
Two ways to give the map its work areas
Work areas come from boundaries you hold (Work areas) or from frames you load.
Load frames before the map appears, for example when the user opens a
project — not from a tap or a camera callback.
Find a frame
If you do not hold frame ids, ask for the frame under a coordinate or under one of your work areas. Both return the frame’s id, outline and box.frameAtLocation and frameFromWorkArea
LociFramesErrorCode.NOT_FOUND.
LociWorkArea.fromBbox(bbox, id) and LociWorkArea.fromFrame(frame) build a
work area from a box or a frame.
Preload your frames
loadFrames returns the frames as work areas and the recorded shapes as
layers. Pass both to the map.
load, then show
LociMapView set the same two values as properties:
show them on the view
properties["frames"], by layer id. Frames start hidden — the user turns
them on in the work-area dialog, or you call controller.showWorkArea(id). A
frame follows every work-area rule, including
geofencing.
What you get back
ALociFramesResult has these properties:
Packs
A pack saves frames on the device under an id you choose, for exampleward-7, so they load without signal. Ids use 1 to 64 letters, digits, _
or -. This is the only place LOCI stores data: a pack holds frame outlines,
layer definitions and baseline values, encrypted and readable only on this
device. It never holds features — the shapes users recorded on a frame, and
the entries your app passes in, are not stored. Pass your features each time.
save, load, refresh, list, delete
What a pack stores: frame id, outline and bounding box; layer definitions
and baseline values, except layers you hid (Hidden layers);
and the creation time, the organisation and the optional maximum age. Packs
are kept out of Android backups and need Android 6.0 (API 23); below
that, pack methods fail with
LociFramesPackError.STORAGE_FAILED. A pack is
never refreshed automatically — call refreshPack.
Expiry. Pass maxAgeSeconds to savePack or refreshPack. After that
long the pack is expired, not deleted, and loadPack fails with
LociFramesPackError.EXPIRED. LociFramesSource.PACK_FIRST treats an expired
pack like a missing one and goes to the service; refreshPack works on it.
With no maxAgeSeconds a pack never expires, and a refresh without it keeps
the pack’s own.
Hidden layers
Keep a layer off the device, such as internal notes, by passing its id ashiddenLayerIds to loadFrames, savePack, loadPack or refreshPack. A
hidden layer is not returned and is not stored in packs.
hide a layer
Source: cloud or pack
loadFrames reads from the service by default. Set source to read a pack.
pack first
savePack.
Errors and events
A failed call reportsLociFramesOutcome.Failure with one of four errors.
Branch on the case and its code, not on the message. None of them carries
your key.
handle a failure
Events
Listen for things worth showing the user.addListener returns a function
that stops listening.
listen
Retry-After and is retried up to three times.
A server error or dropped connection is retried once. Final codes are not
retried. A failed fetch returns no partial result and does not change a pack.
Rate limits and cost
Rate limits apply per key and per organisation, so devices sharing a key share one limit. Load frames in your prepare step, not in a loop. ReadretryAfterSeconds on a RATE_LIMITED error rather than any number here,
because the numbers can change — the full detail is in Limits.

