# Regions

:::note[In one sentence]

An `OctetRegion` is the *area* you ask about (country, city, disc, polygon, bounding box), and its shape determines how the SDK proves containment.

:::

## How shapes map to evidence

Different region shapes are verified by different evidence. A *country* is best proven via the device's mobile country code. The SIM and the serving cell tower both name a country. A *disc* needs a real GPS fix and can fall back to inertial signals. A *city* resolves to a polygon set. Letting callers supply a free-form lat/lon polygon would force the SDK to triangulate and re-quantize every query, which the H3-based proof format does not support. Polygons are therefore accepted only as **H3 cell sets**.

```mermaid
flowchart TD
    R[OctetRegion] --> Named[Named lookups]
    R --> Geometric[Geometric shapes]
    Named --> Country[country isoCode]
    Named --> Sub[subdivision isoCode]
    Named --> City[city name]
    Geometric --> Disc[disc center radius]
    Geometric --> Ellipse[ellipse center axes heading]
    Geometric --> Box[box3D lat lon alt ranges]
    Geometric --> Poly[polygonSet H3 cells]
    Geometric --> Earth[earth maxAltitude]
```

## The shapes

| Factory | Verified via | Typical use |
|---|---|---|
| `earth(maxAltitudeMeters)` | Always `YES`. | Sanity check / fallback. |
| `country(isoCode)` | Fused MCC from serving cell, network operator, SIM home country. | "Is the user in the US?" Works indoors, often without GPS. |
| `subdivision(isoCode)` | Reverse-geocoded admin area on a trusted fix; multi-signal estimator as fallback. | ISO 3166-2 codes: `US-CA`, `FR-75`, `JP-13`. |
| `usState(stateCode)` | Same as `subdivision`. | Sugar for `subdivision("US-XX")`. |
| `city(name)` | SDK resolves the name to an H3 polygon set; proof is verified against that polygon. | "Is the user in San Francisco?" |
| `disc(center, radiusMeters)` | H3-rasterized inside the proof; analytic on the caller side. | "Is the device within 250 m of a saved anchor point?" |
| `ellipse(center, semiMajorM, semiMinorM, headingDeg)` | 2D ground ellipse. Disc is a special case. | GNSS uncertainty ellipses, oriented zones. |
| `box3D(latRange, lonRange, altRange)` | 3D bounding box. | Volumetric containment, building floors. |
| `polygonSet(cells)` | Union of H3 cells. | Custom geofences pre-quantized to H3. |

All factories **validate eagerly**. Invalid lat/lon, malformed ISO codes, non-positive radii, ellipse axes out of order: all trap at construction. An invalid region never reaches a predicate call.

## Why polygons are H3-only

If you have a free-form polygon (a delivery zone, an event venue, a building footprint), you (or your tooling) quantize it to H3 cells ahead of time and pass the cell list. The SDK then uses those cells directly in the proof's Merkle membership circuit. No on-the-fly triangulation. No precision drift. No caller-supplied geometry slipping into the proof.

The H3 cell ID encodes its own resolution. The SDK picks the *coarsest* resolution per query that still satisfies the predicate's accuracy needs. Fine cells when you ask about a city block, coarse cells when you ask about a city.

## Construction helpers (Android only)

On Android, two additional surfaces let you build regions dynamically:

- `getRegion(RegionSpec.country("US"))`. Symbolic lookup, useful when the region name comes from configuration or a server response. The `city` / `namedZone` variants currently throw `OctetFutureFlag` (atlas + server lookup is not yet wired).
- `buildRegion { disc(center = LatLon(37.42, -122.08), radiusMeters = 250.0) }`. A DSL over the static factories for assembling regions from runtime data.

iOS exposes the static factories only at v1. Platform parity for the DSL and `RegionSpec` is on the roadmap.

## Inspecting a region

Two helpers turn a region into a string:

- **`whatisRegion(r)`**. Short, human-readable. For log lines and debug overlays. **Not stable** across SDK versions.
- **`regionToStr(r)`**. Canonical, machine-readable, **stable** wire form. Suitable for log diffs, idempotency keys, eventual `regionFromStr` round-trip.

Both are surfaced uniformly on every API object via `.toStr()`, `.toJson()`, and `.toJsonl()`. See [Serialization](/docs/api-reference/serialization/).

## Where to go next

- [OctetRegion API Reference](/docs/api-reference/octet-region/) for the exact signatures and validation rules.
- [Verdicts](/docs/concepts/verdicts/) for what happens when the cached proof's region is coarser than your query.
