Saltar al contenido principal

Authentication

This page explains how the iframe authenticates and how to load it securely in production. Three credentials are involved. Keep them straight and everything else follows.

The three credentials

CredentialLives inUsed byWhat it is forLifetimeSafe in a URL?
S2S API tokenYour backend onlyYour backendMinting keys and fetching reports (/s2s-api/*)Permanent (issued with your contract)❌ Never
appKeyThe iframe (in memory)The iframeAuthenticating the iframe's own analysis callsDefault long-lived, or minted with a custom expiry⚠️ Reusable while valid
embeddedKeyThe iframe URLYour backend mints it, the iframe consumes itA single-use stand-in that the iframe swaps for an appKeySeconds, single use✅ Yes (recommended)

At a glance, here is how they relate:

The S2S API token never leaves your backend

It is the secret that mints every other key. Only the resulting embeddedKey (or an appKey, if you choose Option B) ever belongs in an iframe URL.

Booting the iframe: two options

You authenticate the iframe by putting a key in its URL. There are two ways to do it.

Option A: embedded key (recommended)Option B: appKey in URL
URL parameter?embeddedKey=…?company=…
What sits in the URLA single-use key that dies on first useA reusable appKey
Backend call per loadYes (POST /iframe-embedded-keys)Optional (only to mint a fresh appKey)
If the URL leaksWorthless, already consumedReusable until it expires
Setup effortSlightly moreMinimal
Use it forProduction, every timeEmbedding inside your own protected app, or quick tests

Your backend requests a brand-new embedded key for every iframe load. It is valid for only 60 seconds by default (configurable) and can be used exactly once, so an intercepted URL, from browser history, a Referer header, a screenshot or a log line, is worthless to an attacker.

You do not implement the swap

Turning the embedded key into the real appKey happens inside the iframe, automatically. Your only job is to mint the embedded key on your backend and put it in the URL. See "Under the hood" if you want to know exactly how the swap works.

Option B: app key in the URL

Put an appKey directly in the URL with the company parameter: https://iframe.legit.health/?company=YOUR_APP_KEY. The iframe uses it as-is, with no exchange step. The appKey can come from two places:

appKey sourceLifetimeWhat to watch for
Default appKey (issued with your contract)Long, but finite: it does expire, the window is simply generousReusable by anyone who captures the URL until it expires
Minted appKey (POST /s2s-api/v3/company-app-keys)Short, a custom expiry you setA captured URL stops working once it expires

This option is simpler to wire up, and it is a perfectly secure choice in the common case.

Option B is not "the insecure option"

In most integrations the iframe is embedded inside your own application and internal resources, which already sit behind your own authentication and access controls. The appKey in the URL is therefore only ever exposed within that protected context, which is enough for the majority of integrators. On top of that, you can mint short-lived appKeys on demand, as many as you want, whenever you prefer a tighter expiry. Reach for Option A when the iframe URL could travel outside that protected boundary, for example public links, shared screens, or third-party analytics.

Endpoint: create an embedded key

Mint one single-use embedded key per iframe load.

MethodPOST
Full URLhttps://{baseUrl}/s2s-api/v3/iframe-embedded-keys
HeadersX-API-TOKEN: <your S2S API token> and accept: application/json

Request body

Both fields are optional. Send an empty body {} to accept the defaults.

FieldDefaultDescription
embeddedKeyExpiresInSeconds~60 secondsWindow between your backend creating the key and the iframe loading with it
appKeyExpiresInSeconds900 (15 min)How long the resulting iframe session stays authenticated. Pass a larger value for longer sessions, e.g. 3600 for a one-hour consultation
curl -X POST "https://{baseUrl}/s2s-api/v3/iframe-embedded-keys" \
-H "X-API-TOKEN: YOUR_S2S_TOKEN" \
-H "accept: application/json" \
-d '{"embeddedKeyExpiresInSeconds": 60, "appKeyExpiresInSeconds": 900}'

Success returns HTTP 201 Created:

{
"embeddedKey": "a-single-use-key-generated-for-this-iframe-load-0123456789abcdef",
"expiresAt": "2026-07-08T12:01:00+00:00"
}

Take the embeddedKey value and place it in the iframe URL as ?embeddedKey=…. The appKey behind it is never part of this response and never appears in any URL.

Endpoint: create an app key

Mint a standalone appKey to use directly in the URL with ?company=… (Option B).

MethodPOST
Full URLhttps://{baseUrl}/s2s-api/v3/company-app-keys
HeadersX-API-TOKEN: <your S2S API token> and accept: application/json

Request body

The body is optional. Send {} to accept the defaults, or set the fields below.

FieldDefaultDescription
Key valueAuto-generated securelyIf you supply your own, it must be at least 64 characters
Expiry15 minutesWhen the appKey stops working
curl -X POST "https://{baseUrl}/s2s-api/v3/company-app-keys" \
-H "X-API-TOKEN: YOUR_S2S_TOKEN" \
-H "accept: application/json" \
-d '{}'

The response returns the appKey. Place it in the iframe URL as ?company=YOUR_APP_KEY.

Under the hood (optional reading)

You do not need any of this to integrate; it is here so you can reason about the security model end to end.

When the iframe loads with ?embeddedKey=…, it makes one call on your behalf to a public exchange endpoint, POST /iframe-key-exchange, sending the embedded key. Our backend performs an atomic single-use claim:

Exchange attemptResult
First call with the embedded keyReturns the real appKey and marks the key as consumed
Any later call with the same key401, the key no longer exists as a credential

This is why single use is guaranteed and cannot be turned off. The iframe keeps the appKey in memory only (never in the URL, localStorage, or sessionStorage) and uses it as its Authorization: Bearer credential for its own API calls, so a replayed URL can never start a second session.

Next Steps

  • Customize the iframe appearance or behavior? See the Customize page.
  • Ready to embed on your platform? See the Platforms section.
  • Need to handle callbacks after analysis? See the Callbacks section.