import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Serialization

The core value types expose three uniform forms. [Where they're available](#where-theyre-available) lists them.

## 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`

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
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,...},...}
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

```kotlin
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,...},...}
```

  </TabItem>
</Tabs>

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`

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
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": { "id": "...", ..., "proof_bytes_b64": "..." },
//   "confidence": { "overall_score": 0, ... },
//   "achievable_level": null
// }
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

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

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

  </TabItem>
</Tabs>

## The `proof` object

`.toJson()` emits the same `proof` object on iOS and Android. It carries the proof's identifying fields and the serialized proof itself:

```json
"proof": {
  "id": "...",
  "level": "COUNTRY",
  "timestamp_ms": 1748462400000,
  "sdk_version": "3.1.2",
  "platform": "ios",
  "spoofing_verdict": "UNSPECIFIED",
  "confidence": { "overall_score": 0, "spoofing_risk_score": 0, "flags": [] },
  "position_commitment_b64": "...",
  "proof_bytes_b64": "..."
}
```

`proof_bytes_b64` is the serialized proof (`LocationProof.proofBytes`), encoded as standard base64 with padding. You can forward the JSON as is. A verifier needs the decoded bytes, so base64-decode `proof_bytes_b64` before you pass it to [`octet-verify`](/docs/mobile/concepts/verifying-proofs/) or [`Octet.verify`](/docs/mobile/api-reference/octet-verify/).

## Changes in 3.1

- **`proof` JSON carries `content_commitments`** when the proof commits content: one entry per item with `key`, `content_type`, `size`, `sha256` in lower-case hex and, if set, `locator`. Never a value or a salt. Other proofs' JSON is unchanged. See [Content Commitments](/docs/mobile/concepts/content-commitments/#how-it-works).
- **No confidence score or tier.** `confidence` holds `0` and empty lists, `spoofing_verdict` is `UNSPECIFIED`, and `toStr()` prints no `conf=`. The keys stay, so a parser keeps working. See [`OctetVerdict`](/docs/mobile/api-reference/octet-verdict/#confidence).

## Changes in 3.0

- **`proof` JSON carries `spoofing_verdict`.** It is the proof's assurance tier (`VERIFIED`, `PLAUSIBLE`, and so on), the same value as `LocationProof.spoofingVerdict`, so you can read it without decoding the proof bytes. See [`OctetVerdict`](/docs/mobile/api-reference/octet-verdict/#the-assurance-tier).

## 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 from 2.0), a proof binds the device's own inside/outside answer for its claimed region, and the city and earth geometry. This lives inside `proofBytes`, checked by [`octet-verify`](/docs/mobile/concepts/verifying-proofs/) and [`Octet.verify`](/docs/mobile/api-reference/octet-verify/). It is not a separate JSON field.

When a predicate runs with a `sessionNonce` (see [Proof Binding](/docs/mobile/concepts/proof-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, to forward proof material to a verifier, 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:

```kotlin
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

- [`OctetRegion`](/docs/mobile/api-reference/octet-region/)
- [`OctetVerdict`](/docs/mobile/api-reference/octet-verdict/)
- [License Types](/docs/mobile/api-reference/license-types/). License types have no `.toStr()`, `.toJson()` or `.toJsonl()`. Read fields directly.
