Docs/API Reference/Serialization

Serialization

Every public API object exposes three uniform forms.

The three methods

Method Purpose Stability
.toStr() Single-line human-readable summary Not stable. For logs and debug UI only.
.toJson() Pretty JSON, full structure Stable. HTTP bodies, files, verifier input.
.toJsonl() Single-line JSON, no whitespace Stable. Line-delimited log sinks, streaming.

Where they're available

.toStr() / .toJson() / .toJsonl() are defined on:

  • OctetRegion
  • OctetVerdict
  • LatLon
  • H3Cell
  • Interval

Examples

OctetRegion

let r = OctetRegion.disc(center: LatLon(37.422, -122.084), radiusMeters: 250)
r.toStr()    // "Disc at (37.4220, -122.0840), r=250 m"
r.toJsonl()  // {"shape":"ellipse","center":{"latitude":37.422,...},...}
val r = OctetRegion.disc(LatLon(37.422, -122.084), 250.0)
r.toStr()    // "Disc at (37.4220, -122.0840), r=250 m"
r.toJsonl()  // {"shape":"ellipse","center":{"latitude":37.422,...},...}

Discs round-trip as ellipses in the JSON form (the underlying type is EllipseRegion with equal axes). The human-readable .toStr() form keeps the "Disc" label for readability.

OctetVerdict

verdict.toStr()
// "YES reason=OK atTime=2026-05-28T15:30:00.000Z validity=[...] conf=0.92"

verdict.toJson()
// {
//   "result": "YES",
//   "reason": "OK",
//   "message": "isWithin evaluated on cached proof",
//   "queried_at": "2026-05-28T15:30:00.000Z",
//   "validity": { "from": "...", "to": "..." },
//   "proof": { "__future_flag__": "...", "id": "...", ... },
//   "confidence": { "__future_flag__": "...", "overall_score": 0.92, ... }
// }
verdict.toStr()
// "YES reason=OK atTime=2026-05-28T15:30:00.000Z validity=[...] conf=0.92"

verdict.toJson()
// {
//   "result": "YES",
//   ...
// }

v1 caveat: proto-backed fields are placeholders

LocationProof and ConfidenceSummary are protobuf-backed types. .toJson() does not emit their full proto-JSON encoding, so that iOS and Android produce the same shape (the Android side uses protobuf-javalite, which lacks JsonFormat).

For v1, .toJson() emits a placeholder object for those nested structures:

"proof": {
  "__future_flag__": "OCTET_FUTURE_FLAG :: LocationProof full proto-JSON",
  "id": "...",
  "timestamp_ms": 1748462400000,
  "sdk_version": "2.0.0",
  "platform": "ios"
}

The __future_flag__ marker identifies a field whose full encoding is not emitted here. Identifying fields (id, timestamp_ms, sdk_version, platform) are populated, so log diffs and routing remain useful.

Changes in 2.0

  • confidence.flags JSON is UPPER_SNAKE on iOS. iOS previously emitted Swift case names (vpnActive). It now emits VPN_ACTIVE, matching Android. This is a breaking change for code that parses toJson() / toJsonl() on iOS. The typed ConfidenceSummary.flags and toStr() are unchanged.
  • Proofs commit a location_verdict. Under semantic-binding v2 (the default in 2.0), a proof binds the honest inside/outside outcome and city/earth geometry. This lives inside proofBytes, checked by octet-verify and Octet.verify, not surfaced in the placeholder JSON above.

When a predicate runs with a sessionNonce (see Session-binding), the proof material commits a hash of that nonce. Only the hash is serialized, never the raw nonce.

The stability commitment in the table above applies to non-proto fields today. The proto fields become stable when the proto-JSON path lands. Until then, integrators who need to forward proof material to a verifier should pass the LocationProof value itself (it is a typed proto on both platforms), not verdict.toJson().

Helper free functions (Android)

In addition to the methods, Android exposes:

fun whatisRegion(r: OctetRegion): String   // equivalent to r.toStr(); NOT stable
fun regionToStr(r: OctetRegion): String    // STABLE canonical machine form

regionToStr is the stable wire form behind region.toJson()'s shape, iso_code, and other fields. Same content, more compact representation, suitable for log diffs and idempotency keys.

iOS exposes only the method form: region.toStr().

See also