Minting an Anytime tile grant

Create, read, and revoke Popcast Anytime 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 Popcast Anytime 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 .../anytime/{layer}/{z}/{x}/{y}.mvt) are metered, at 1 credit per 1000 tiles.

Prerequisites

  • Your organization must be licensed for the popcast.anytime.segment_occ_tiles feature. This feature is manual-grant-only: an explicit grant row is required. Unlicensed orgs get 403 FEATURE_NOT_LICENSED. Contact sales for an explicit grant.
  • Credentials: a Supabase JWT (Authorization: Bearer <jwt>) or an org-scoped X-API-Key. Anonymous callers cannot mint grants.

Create a grant

POST /v2/popcast/tiles/anytime/grants

Request body:

FieldTypeMeaning
namestring (max 120)Human-readable label, shown in the developer dashboard
tilesetsstring[] (max 12)Tileset slugs from the discovery catalog. An empty array means all twelve published tilesets; the persisted grant carries the expanded concrete set. An unknown slug is rejected with 403 TILE_LAYER_NOT_LICENSED.
allowed_originsstring[]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_atISO 8601 datetimeGrant 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/popcast/tiles/anytime/grants" \
  -H "X-API-Key: <your-org-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q3 campaign map",
    "tilesets": [],
    "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/popcast/tiles/anytime/grants/01J5XH9K3W2Q5M0N8P7R4T6VYC/tilejson",
    "tilesets": ["anytime_21d529097c81af04_202501-202512_bg_tiles_v4", "..."],
    "allowed_origins": ["https://maps.example.com"],
    "expires_at": "2026-12-31T00:00:00Z",
    "created_at": "2026-08-08T20: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-tilesets: any of the twelve published slugs implies access to all of them. There is no per-segment scoping.

List, read, revoke

OperationEndpointNotes
List grantsGET /v2/popcast/tiles/anytime/grantsCaller-org's grants, most recent first. Each grant carries usage_30d (placeholder; reported as 0 today, outbox aggregate lands in a follow-up).
Read oneGET /v2/popcast/tiles/anytime/grants/{id}Scoped to the caller's org; expired or foreign grants read as 404.
RevokePOST /v2/popcast/tiles/anytime/grants/{id}/revokeMarks the grant revoked immediately. Caveat: tile-token JWTs already minted by the TileJSON endpoint remain valid until they expire (max 24h).

Error cases

  • 403 FEATURE_NOT_LICENSED: org has no explicit grant for popcast.anytime.segment_occ_tiles. Contact sales.
  • 403 TILE_LAYER_NOT_LICENSED: a tilesets[] entry names a slug Popcast Anytime does not publish. Fires on unknown slugs only, never as a licensed-subset rejection (the entitlement is all-tilesets).
  • 403 TILE_GRANTS_REQUIRE_METERED: org has no metered Stripe billing rail. Under the deployed accumulate meter mode this code is unreachable (credit-pack wallets are billed in-transaction); the license gate is the sole gate in practice.
  • 403 ORIGIN_NOT_ALLOWED: the fetch's Origin is not in allowed_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 popcast.anytime.segment_occ_tiles.",
    "status": 403,
    "request_id": "req_e8281bd0f5a1",
    "product": "popcast",
    "context": {}
  }
}

Expired grants read as 404 NOT_FOUND (Grant expired.), 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-token JWTs already minted by the TileJSON endpoint remain valid until they expire (max 24h) unless the grant's tileset access is revoked earlier.

Token lifecycle

Each GET /tilejson mints a fresh tile-token JWT valid for 24 hours, embedded in tiles[]. Tokens are signed kid: popcast-anytime-v1; cross-product tile tokens are structurally rejected at verification, so a Popcast Anytime token never authorizes another product's tiles. 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.


Did this page help you?