Skip to Content
EmbedsKeys & domains

Keys & domains

Embeds run in your visitors’ browsers, so their API key is visible to anyone who views source. That is expected and safe — provided you use a publishable key and restrict it to your domains.

Never put a secret key in an embed. Secret keys carry full permissions and are meant for servers you control. If you have pasted one into a web page, revoke it in the dashboard now and create a publishable key instead.

Creating the key

In the Ullr dashboard: Integrations → API keys → Create key.

  • Type: Publishable
  • Scopes: see the table below
  • Allowed domains: your site’s hostnames

You can also create a key from any embed’s configurator page, which pre-selects the “All embeds” preset — trails:read, lifts:read, features:read, resorts:read and updates:read. That preset deliberately contains live-status scopes only: no history, no snapshots.

Publishable keys remain visible in the dashboard after creation, so you can re-copy a snippet any time.

Scopes each embed needs

EmbedScopes
Trail listtrails:read
Lift listlifts:read
Interactive mapresorts:read + one per layer you turn on: trails:read, lifts:read, features:read
Conditions widgetresorts:read, trails:read, features:read, updates:read, plus lifts:read when showLifts is on

The two list embeds need only their own scope — each reads a single endpoint and never asks for the area itself. The map and the widget both load the area, so they also need resorts:read.

One key can serve every embed on your site. Give it the union of what those embeds need and nothing more.

Leave the history scopes (trails:readHistory, lifts:readHistory, features:readHistory) and the snapshot and summary scopes off an embed key unless you are deliberately using the timestamp attribute. They expose your operational record over time to anyone who copies the key out of your page — a live-only key leaks today’s status, a history key leaks the whole season.

The one case that needs them is a historical view: setting timestamp on an embed switches it onto the history endpoints, so that key needs readHistory for each resource the embed reads instead of the live scope. Give that its own key, restricted to the page that uses it, rather than widening the key serving your public status boards.

Restricting domains

An unrestricted publishable key works from anywhere — including someone else’s website, on your data and your quota. Set the allowed domains.

Enter bare hostnames, one per entry:

example.com *.example.com
  • No scheme, port or path. https://example.com/ is invalid; use example.com.
  • One leading wildcard label is allowed. *.example.com matches www.example.com and conditions.staging.example.com, but not the bare example.com. List both when you need both.
  • Localhost is always allowed — localhost, 127.0.0.1 and [::1] on any port — so local development needs no extra entry.
  • An empty list means no restriction. Fine while prototyping; fix it before you ship.

Matching is against the browser’s Origin header. A request with a restricted key and no Origin at all — plain curl, most server-side code — is denied. That is deliberate: publishable keys are for browsers.

Rotating a key

  1. Create the new key with the same scopes and domains.
  2. Update your pages to the new key and deploy.
  3. Watch Last used on the old key in the dashboard — it stops advancing once nothing is using it. (It updates at most once an hour, so give it time.)
  4. Delete the old key.

If a key has leaked and you need it dead immediately, revoke it — that takes effect at once, and you can create the replacement afterwards.

What a rejected embed looks like

An embed whose key is refused renders nothing rather than an error box, so check the browser’s network tab:

StatusCause
401Key missing, misspelled, revoked, or expired
403Key lacks the scope for that data, or your domain is not on its allowed list

Troubleshooting walks through each case.

Last updated on