Minting a Viewcast tile grant
Create, read, and revoke Viewcast tile-delivery grants. Grants are the capability that unlocks the TileJSON URL your map client consumes.
Tile access runs through grants: a minted record that authorizes your organization to fetch Viewcast vector tiles. A grant returns a tile_json_url capability URL you drop into MapLibre or Mapbox GL JS.
All grant management endpoints are free (0 credits): grant CRUD and the TileJSON fetch are control-plane operations. Only the tile bytes (GET .../viewcast/tiles/{layer}/{z}/{x}/{y}.mvt) are metered, at 1 credit per 1000 tiles.
Prerequisites
- Your organization must be licensed for the
viewcast.profiles.inventory_tilesfeature and on a metered billing rail. Unlicensed orgs get403 FEATURE_NOT_LICENSED; credit-pack-only orgs get403 TILE_GRANTS_REQUIRE_METERED. Contact sales for access. - Credentials: a Supabase JWT (
Authorization: Bearer <jwt>) or an org-scopedX-API-Key. Anonymous callers cannot mint grants.
Create a grant
POST /v2/viewcast/tiles/grants
Request body:
| Field | Type | Meaning |
|---|---|---|
name | string (max 120) | Human-readable label, shown in the developer dashboard |
tilesets | string[] | Layer slugs to include, from the discovery catalog: viewsheds, paths_vehicle, paths_pedestrian, gates_vehicle, gates_pedestrian. Any other slug is rejected with 403 TILE_LAYER_NOT_LICENSED. |
allowed_origins | string[] | Browser Origin allowlist for TileJSON and tile fetches. Wildcards are not supported; list each origin literally. Use the explicit string "null" to allow requests that omit Origin (server-side rendering, curl). |
expires_at | ISO 8601 datetime | Grant expiry. Note the tile-token inside the TileJSON independently expires 24h after each mint; re-fetch TileJSON before then. |
Example:
curl -X POST "https://api.mworks.com/v2/viewcast/tiles/grants" \
-H "X-API-Key: <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"name": "Q3 inventory map",
"tilesets": ["viewsheds", "paths_vehicle"],
"allowed_origins": ["https://maps.example.com"],
"expires_at": "2026-12-31T00:00:00Z"
}'
Response (201):
{
"data": {
"grant_id": "01J5XH9K3W2Q5M0N8P7R4T6VYC",
"tile_json_url": "https://api.mworks.com/v2/viewcast/tiles/grants/01J5XH9K3W2Q5M0N8P7R4T6VYC/tilejson",
"tilesets": ["viewsheds", "paths_vehicle"],
"allowed_origins": ["https://maps.example.com"],
"expires_at": "2026-12-31T00:00:00Z",
"created_at": "2026-08-10T20:05:11Z"
}
}The tile_json_url is a capability URL: treat it like an AWS S3 presigned URL. The opaque grant_id segment IS the credential; anyone holding the URL can render tiles from an allowed origin until the grant expires or is revoked. Two safety nets are built in: allowed_origins pins which sites can fetch tiles (Origin-enforced), and revoke kills the grant.
The entitlement is one-feature-all-layers: any of the five published slugs implies access to all of them. There is no per-layer scoping.
List, read, revoke
| Operation | Endpoint | Notes |
|---|---|---|
| List grants | GET /v2/viewcast/tiles/grants | Caller-org's grants, most recent first. Each grant carries usage_30d (placeholder; reported as 0 today, outbox aggregate lands in a follow-up). |
| Read one | GET /v2/viewcast/tiles/grants/{id} | Scoped to the caller's org; expired or foreign grants read as 404. |
| Revoke | POST /v2/viewcast/tiles/grants/{id}/revoke | Marks the grant revoked immediately. Caveat: tile tokens already minted by the TileJSON endpoint remain valid until they expire (max 24h). |
Error cases
403 FEATURE_NOT_LICENSED: org is not licensed forviewcast.profiles.inventory_tiles. Contact sales.403 TILE_GRANTS_REQUIRE_METERED: org has no metered Stripe billing rail (credit-pack only). Contact sales to enable Viewcast vector-tile delivery.403 TILE_LAYER_NOT_LICENSED: atilesets[]entry names a slug Viewcast does not publish.403 ORIGIN_NOT_ALLOWED: the fetch'sOriginis not inallowed_origins.400: invalid request body.401: missing or invalid credentials.
Deny envelope: every error response on this surface uses the standard envelope shape. Example for an unlicensed org:
{
"error": {
"code": "FEATURE_NOT_LICENSED",
"message": "Your organization is not licensed for viewcast.profiles.inventory_tiles.",
"status": 403,
"request_id": "req_e8281bd0f5a1",
"product": "viewcast",
"context": {}
}
}Expired grants read as 404 NOT_FOUND, not 403 or 410: a foreign, nonexistent, or expired grant id is indistinguishable from the outside. Revocation deletes the served record (subsequent reads 404), while tile tokens already minted by the TileJSON endpoint remain valid until they expire (max 24h).
Token lifecycle
Each GET /tilejson mints a fresh opaque tile token valid for 24 hours, embedded in the manifest's tiles[] URLs. The manifest's x-mw.refresh_after (82,800 seconds, 23h) tells clients when to re-fetch the TileJSON for a fresh token. Standard TileJSON consumers append the ?token= query to every tile request automatically; you do not call the .mvt endpoint by hand, and the token is accepted only as a query parameter, never as a header.
Updated about 15 hours ago