# Bitbop Map API 1.1

Base URL: https://map.bitbop.dev. Machine contract: `/openapi.json`. Interactive explorer: `/docs`. Start with `/v1/capabilities`; it describes the actual current release, source resolutions, access rules, limits and unsupported services. Deployment and data releases are independent: installing API 1.1 does not publish new terrain.

The explorer bundles Swagger UI 5.34.1 locally with its Apache-2.0 licence, notices and source hashes. It does not fetch a mutable third-party script or send your contract to an external validator. Authorization entered in the explorer is not persisted.

## Request workflow

1. Discover capabilities and `/v1/layers`. Never assume that requesting 10 or 30 metres means that resolution exists at a location.
2. Resolve a point or H3 cell with `/v1/resolve`, or inspect a viewport using `/v1/coverage`.
3. Pin the returned `release_id` for subsequent lookups and pagination. Load its signed manifest once. Verify the exact decoded payload bytes with an independently provisioned Ed25519 public key. The development key is only for fixtures.
4. Match every selected descriptor against that verified manifest. Download `/v1/objects/{sha256}`, verify byte length and SHA-256 of the complete encoded bytes, then decode using the signed grid/CRS/encoding. HTTP ranges need reassembly before full-object verification.
5. Display credits from the verified release. Treat absent, masked and coarser data explicitly; zero is a valid elevation, not missing data.

The resolver includes a convenient server-computed nearest-native sample. This is an unsigned hint, not independently authenticated evidence. `POST /v1/samples` provides the same convenience for a bounded batch. The example client instead verifies and decodes complete objects. API payloads use `{lat, lon}`; Co-op Rider uses `{lat, lng}`. Bboxes are `west,south,east,north`, and GeoJSON coordinates are `[longitude, latitude]`.

## Endpoint reference

| Endpoint | Purpose | Access |
|---|---|---|
| `GET /healthz`, `/readyz` | Process liveness and publication readiness | Public |
| `GET /v1/capabilities` | Version, current release, limits, links and non-features | Public |
| `GET /v1/catalog?limit=20&offset=0` | Retained release catalog; optional `region=world\|thule\|synthetic`, `bbox` | Public metadata |
| `GET /v1/layers?release=…` | Native/delivered spacing, source IDs, encodings and vertical references | Public metadata |
| `GET /v1/coverage?bbox=…&layers=elevation&limit=50&offset=0&release=…` | Paginated intersecting signed descriptors, no raster reads | Public metadata |
| `GET /v1/resolve?lat=…&lon=…&layers=elevation,imagery&spacing_m=30&release=…` | Per-layer point selection and convenient sample | Public pilot or Bitbop grant |
| `GET /v1/resolve?cell=…&layers=elevation&spacing_m=30&release=…` | Every overlapping requested-layer object, including halos | Public pilot or Bitbop grant |
| `POST /v1/samples` | Up to 64 points and 8 unique layers against one release | Public pilot or Bitbop grant |
| `GET /v1/h3?lat=…&lon=…&resolution=9&ring=1` | Actual H3 address, centre, boundary and neighbouring cells | Public; no presence verification |
| `GET /v1/h3?cell=…&ring=1` | Inspect an existing cell; omit lat/lon/resolution | Public |
| `GET /v1/releases/{id}/manifest` | Immutable signed release envelope | Public |
| `GET /v1/releases/{id}/credits` | Release source notices; also inside signed manifest | Public |
| `GET, HEAD /v1/objects/{sha256}` | Exact immutable bytes; single ranges and ETags | Per-object scope |
| `GET /v1/docs`, `/v1/docs/{api\|architecture\|game\|taxi\|mcp}` | Packaged guides from this software build | Public |
| `GET /openapi.json`, `/v1/openapi.json`, `/schemas/manifest-v1.json` | API and manifest schemas | Public |
| `/mcp` | Streamable HTTP integration MCP; separate protocol | Public-only |

Example public Thule lookup:

```sh
curl 'https://map.bitbop.dev/v1/resolve?lat=76.52&lon=-68.7&layers=elevation&spacing_m=10'
curl 'https://map.bitbop.dev/v1/h3?lat=-37.8136&lon=144.9631&resolution=9&ring=1'
```

Batch body (`Content-Type: application/json`):

```json
{"points":[{"lat":-37.8136,"lon":144.9631},{"lat":-27.5598,"lon":151.9507}],"layers":["elevation"],"spacing_m":30}
```

Authenticated JavaScript (the key comes from your existing sign-in flow):

```js
const response = await fetch('/api/map/v1/samples', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', Authorization: `Bearer ${key}`},
  body: JSON.stringify({points, layers: ['elevation'], spacing_m: 30}),
  signal: controller.signal
});
if (!response.ok) throw new Error(`Map HTTP ${response.status}`);
const {release_id, results} = await response.json();
```

`/api/map` above is an application-owned same-origin relay, not a route already installed on Map. It must allowlist Map paths/methods, bound bodies, forward each caller's header without substituting a shared key, omit cookies, disable caching of protected/location responses, and preserve upstream status. Map does not enable arbitrary cross-origin CORS. A native/server client can call Map directly; browser applications need a same-origin relay or an explicitly configured origin policy. Do not put credentials in URLs or persistent browser storage.

## Selection, coordinates and missing data

Point preference: exact native spacing, nearest finer spacing, nearest coarser spacing; ties follow source priority and digest. A masked source falls through. `status` is `available`, `coarser` or `absent`. Cell and coverage queries return candidates rather than a resampled mosaic. Coverage bboxes crossing the date line use west > east. Returned footprint intersection does not promise every raster sample is populated.

`native_spacing_m` is nominal source spacing; `delivered_spacing_m` is the delivered nominal spacing. Exact pixel steps are in `grid.pixel_size` and `grid.coordinate_unit`. Latitude/longitude grids do not have constant metre spacing. The current format supports row-major `int16-le` heights with a declared sentinel and `rgba8` imagery with transparent missing pixels. Signed `horizontal_crs` and `vertical_reference` must survive decoding. H3 is an index over WGS84 locations, not a datum transformation. Do not combine ellipsoidal, geoid and tidal heights without the appropriate transformations.

Global 30 m land coverage remains a publication objective, not the current production guarantee. The deployed global fallback is roughly 500 m, with a real 10 m Thule pilot. Bathymetry and imagery have independent native resolutions. Consult live capabilities and collection evidence for the exact release; source availability, licence gates and capacity are tracked separately.

## Limits, errors and retries

Catalog/coverage pages: 1–100; pin release on coverage pages. Resolver layers: at most 32 unique names. Batch: 1–64 points, 1–8 unique layers, 32,768 bytes including chunked bodies. H3: resolutions 0–15 and rings 0–3; a disk includes the centre and can contain fewer cells near pentagons. H3 distance is not road distance. Spacing must be positive and finite, coordinates finite and within WGS84 bounds. Recognised duplicate query parameters are rejected.

| Status | Meaning / client action |
|---|---|
| 400 | Invalid input; correct it, do not retry unchanged |
| 401 | Missing/invalid/revoked credential; sign in through Bitbop |
| 403 | Identity valid but no Map grant; check entitlement |
| 404 | Unknown release/object/guide; refresh discovery deliberately |
| 413 | Request or MCP response too large; split/narrow it |
| 416 | Invalid/multiple/unsatisfiable byte range |
| 503 | Required verifier or published data unavailable; bounded backoff |

Gateway-wide rate limiting is deployment-specific, not a guaranteed per-user quota in this build. Clients should bound concurrency and bytes, cancel stale movement queries, respect any upstream `Retry-After`, and use jittered backoff for transient failures. Do not silently replace 401/403 with an unrestricted download path. Batch responses are atomic: a required protected answer without access fails the request; no partial protected response is emitted.

## Identity, caching and privacy

The same existing Bitbop bearer credential used across products is verified by the central identity service. Stripe manages commercial membership outside Map. The current owner policy grants Map to verified API-key tiers; no local parallel subscription system exists. Verifier failure fails closed. Authorization precedes protected body, HEAD, range and conditional replies, including warm object caches.

Public objects and pinned manifests are immutable with long HTTP freshness. Protected responses are `private, no-store`; samples and coordinate queries are not shared-cache entries. The reference client's protected byte reuse is bounded session memory and is cleared on key change/logout. Persistent protected/offline sharing needs a separate explicit entitlement design and is not implemented here. Browser storage persistence is best effort: request persistence where appropriate, report quota, survive eviction and revalidate hashes. Never promise that browser data cannot be evicted or that downloaded plaintext can be remotely revoked.

The game may later try verified local objects, authorised room peers and origin in that order. An object digest proves content, not permission. Only public objects with an explicit approved redistribution record are eligible for the optional example peer callback. Community OSM tiles are a separate provider policy and are excluded from offline packs and peer distribution.

Queries can contain locations. Map runs without access logging; an application's reverse proxy, Cloudflare and observability settings still need the same minimisation. Map stores raster/catalog data, not driver positions, trip histories, phone numbers or a trust graph.

## Versioning and extension

`/v1` is the API major version; `api_version` describes additive capabilities. `bitbop-map/1` is the independently versioned signed manifest schema. Immutable release IDs bind data and credits. New layers and spacings can arrive without changing the game protocol. Clients must handle unknown capability fields and absent optional layers; unsupported object encodings/CRSs must fail explicitly. A breaking wire/schema change requires a new major version. Public key rotation requires an independently delivered trust update, not trusting a key embedded in the manifest it signs.

## Transport integration boundary

Co-op Rider computes real locations locally and uses its own cached street provider, area lobbies, device admission, private TURN binding and receipt service. Map supplies optional terrain/imagery and generic catalogue/H3 APIs. It does not dispatch drivers, store rides or attest proximity. The runnable taxi example returns application discovery identifiers rather than pretending Map hosts WebSocket rooms. See [taxi integration](taxi-integration.md).
