Skip to content

Give out a product public key

Create, scope and revoke the keys that let a caller reach the routes your toolkits publish without a platform login.

Some routes a toolkit publishes are served without a platform login — an embed, a widget callback, a public metadata endpoint. A toolkit declares each such route as either open or keyed. An open route answers anyone. A keyed route answers only a caller that presents a product public key.

Public Access, under a product’s settings, is where those keys live. You create them, scope them, and revoke them there; the platform checks them at the edge, so the toolkit itself never sees a key and never validates one.

A key belongs to one product. It is not shared with other products, even in the same organization.

Only a product Owner or Admin can open the section. Everyone else gets the standard no-permission state.

Read the key list

The page is the list of the product’s public keys. An agent reads the same list with organization_public-keys_list, which never returns key material, and enumerates what the keys can reach with organization_public-routes_list — every public route the product’s toolkits declare, with its access mode and public URL.

Each row shows:

ColumnWhat it holds
NameThe name given at creation
KeyThe key’s prefix followed by dots — the rest is never shown again
ToolkitsThe toolkits the key reaches, or All toolkits
CreatedThe date the key was created
StatusActive, or Revoked for a key that has been revoked

A key adopted from a toolkit’s own keys carries an Imported badge next to its name; see Imported keys.

A revoked key stays in the list, badged and without actions, so you can see that the revocation landed.

With no keys yet, the page says so: keyed access stays closed until a key that covers it exists.

Create a key

Create public key asks for a name and a scope. On save the platform shows the key once, in plaintext, behind a “you won’t be able to see this again” notice with a copy button.

A minted key looks like lpk_ followed by 32 hexadecimal characters. The platform stores only its SHA-256 and the first characters of its body, so a lost key cannot be recovered — you create a new one and revoke the old.

There is no rotation. Rotating a key means creating a second key, moving callers onto it, then revoking the first.

From an agent the same mint is organization_public-keys_create; the plaintext is returned only in that one response and cannot be retrieved again.

A new key starts answering within about 30 seconds; see Changes take about 30 seconds.

Scope the key

A key’s scope is chosen by toolkit, as a radio:

  • All toolkits — every public route of the product, including routes added later. Broad; prefer a narrower key for anything handed to a third party.
  • Selected toolkits — one checkbox per toolkit that publishes public routes. The key reaches every keyed route of the toolkits you tick and nothing else.

Edit scope on a row replaces the scope of an existing key — organization_public-keys_update-scope from an agent. The key’s value does not change.

The management API can also scope a key to individual routes. Such a key is listed under its toolkits with the note limited to specific routes. Opening Edit scope on it shows the editor at toolkit level and warns that saving widens the grant.

Revoke a key

Revoke asks for confirmation, then retires the key. Keyed routes stop answering it within about one propagation window — roughly 30 seconds.

From an agent it is organization_public-keys_revoke.

Revocation is permanent and idempotent: there is no un-revoke, and a revoked key is never resurrected. To restore access, create a new key.

Send a key on a public route

Most toolkits publish their public routes on one shared edge: public.lessly.com in production, public.lessly.dev in staging. On that edge a product is addressed by the first path segment rather than by a host of its own, followed by the toolkit that publishes the route:

https://public.lessly.com/{product_id}/<toolkit>/...

On a keyed route the platform reads the key from the first of these headers that is present:

  1. Authorization: Bearer <key> — use this for new integrations
  2. X-Public-Key: <key>
  3. X-Api-Key: <key> — a legacy alias, supported so callers that already use it keep working
curl "https://public.lessly.com/$PRODUCT_ID/realtime/widget/42" \
  -H "Authorization: Bearer lpk_0123456789abcdef0123456789abcdef"

The order is by presence, not by validity: a wrong key in a higher-priority header is rejected, not a reason to look at the next header. An Authorization header carrying any other scheme is ignored, so a browser’s Basic prompt is never mistaken for a key.

Once the key checks out, all three headers are deleted before the request reaches the toolkit. The handler receives no credential.

Open routes take no key. CORS, rate limits and body caps apply the same way on open and keyed routes.

Errors on a keyed route

HTTPCodeMeaning
401public_key_requiredNo key was presented
401public_key_invalidThe key matches no active key of this product
403public_key_scopeThe key is valid but its scope does not cover this route
503public_config_unavailableThe platform edge has not loaded its key snapshot yet — fail-closed; retry

A revoked key surfaces as 401 public_key_invalid once the change has propagated. 404, 413 and 429 keep their usual meanings — unknown route, body over the cap, rate limit — and have nothing to do with keys.

Changes take about 30 seconds

Keys are served from a snapshot the platform edge refreshes every 30 seconds or so. Creating a key, changing its scope and revoking it all take effect within one such window rather than instantly. Wait out the window before testing a fresh key, and say so in whatever onboarding copy you hand to the people using the key.

Imported keys

Toolkits used to issue their own keys for their public routes — rtk_… for realtime, lmk_… for mail, sk_live_… for tracking. When a toolkit moved its routes to keyed access, the platform copied those keys into the product’s key list, and they keep working unchanged.

  • The value is the same. An imported key keeps its original prefix and its original value — an rtk_… key stays rtk_… and is not reshaped into lpk_…. Nothing was re-issued, so no integration had to be reconfigured.
  • The header is the same, for callers already sending Authorization: Bearer, X-Public-Key or X-Api-Key. A caller that used some other custom header has to move to one of the three.
  • They are marked. Imported keys carry an Imported badge, with the tooltip Migrated from the toolkit’s own keys. They keep the name and creation date they had, an unnamed one falling back to Imported from <slug>, and they list no member as their creator.
  • They are scoped to their own toolkit — every keyed route of the toolkit they came from, and nothing else. A finer permission the toolkit tracked privately did not survive the copy.
  • They are ordinary keys from then on. An Owner or Admin can widen or narrow their scope and revoke them, exactly as with a minted key. The plaintext is not shown, the same way it is not shown for a minted key after creation.

Next steps

Was this page helpful?
Esc

Start typing to search the docs.

navigateselect