Minting a Displays tile grant

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

Prerequisites

  • Your organization must be licensed for the oohdisplays.displays.inventory_tiles and oohdisplays.displays.face_point_tiles features. Tile traffic is charged to your organization at 1 credit per 1,000 tiles served; if your billing setup cannot carry tile traffic, grant creation returns 403 TILE_GRANTS_REQUIRE_METERED; contact sales to enable tile delivery.
  • Credentials: a signed-in session token (Authorization: Bearer <token>) or an org-scoped X-API-Key. Anonymous callers cannot mint grants. Grants are attributed to the user who created them, so an API key must have been created by a user (see API_KEY_NO_AUDIT_USER below).

Create a grant

POST /v2/displays/tiles/grants

Request body:

FieldTypeMeaning
namestring (max 120)Human-readable label, shown in the developer dashboard
tilesetsstring[]Layer slugs to include, from the discovery catalog: faces, viewsheds, paths_vehicle, paths_pedestrian, gates_vehicle, gates_pedestrian. Any other 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://api2.mworks.com/v2/displays/tiles/grants" \
  -H "X-API-Key: ***" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q3 inventory map",
    "tilesets": ["faces", "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://api2.mworks.com/v2/displays/tiles/grants/01J5XH9K3W2Q5M0N8P7R4T6VYC/tilejson",
    "tilesets": ["faces", "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 six published slugs implies access to all of them. There is no per-layer scoping.

List, read, revoke

OperationEndpointNotes
List grantsGET /v2/displays/tiles/grantsYour organization's grants, most recent first. Each grant carries usage_30d, reported as 0 today until usage reporting ships.
Read oneGET /v2/displays/tiles/grants/{id}Scoped to the caller's org; expired or foreign grants read as 404.
RevokePOST /v2/displays/tiles/grants/{id}/revokeRevokes the grant immediately. New TileJSON requests for it fail with 404, and tile requests that use tokens already minted from it are refused as well (401 UNAUTHORIZED with error.context.reason: grant_revoked), so revoking is the instant cutoff for a leaked URL. Revoking an already-revoked grant returns 200 with the unchanged record.

Error cases

  • 403 FEATURE_NOT_LICENSED: org is not licensed for oohdisplays.displays.inventory_tiles or oohdisplays.displays.face_point_tiles. Contact sales.
  • 403 TILE_GRANTS_REQUIRE_METERED: tile delivery for your organization requires pay-as-you-go (metered) billing, which is not enabled. Contact sales to enable tile delivery.
  • 403 TILE_LAYER_NOT_LICENSED: a tilesets[] entry names a slug Displays does not publish.
  • 403 API_KEY_NO_AUDIT_USER: the API key is not tied to a user, so the grant cannot be attributed. Mint with a signed-in session, or with an API key created by a user.
  • 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 oohdisplays.displays.inventory_tiles.",
    "status": 403,
    "request_id": "req_e8281bd0f5a1",
    "product": "displays",
    "context": {}
  }
}

Expired grants read as 404 NOT_FOUND, not 403 or 410: a foreign, nonexistent, or expired grant id is indistinguishable from the outside. A revoked grant stays readable through the grant endpoints with revoked_at set, but its TileJSON returns 404 and every tile request made with a token minted from it is refused with 401 UNAUTHORIZED (grant_revoked), even if that token has not yet expired.

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. Revoking the grant invalidates outstanding tokens immediately (see above).


Did this page help you?