Developer documentation
Private viewer grants
An existing server-signed access path, separate from owner API keys and browser sign-in.
Configure private access
- Select Private under Access in the owner publishing panel.
- Save draft to generate the experience signing secret if none exists.
- Copy the one-time Viewer signing secret display into your server secret storage.
- Publish the experience after configuring its allowed domains.
- Authenticate the viewer on your own server.
- Authorize that viewer to access the selected experience before issuing a grant.
If the secret is lost, Rotate secret creates and displays a replacement. There is no browser-readable secret lookup. This signing secret is not an account API key.
Grant format
The payload contains experience_id, viewer_id, exp and nonce. Encode its UTF-8 JSON as base64url, sign that encoded string with HMAC-SHA256, and append the base64url signature after a dot.
| Field | Constraint |
|---|---|
| experience_id | The exact experience UUID. |
| viewer_id | Stable, nonblank viewer identifier; at most 200 characters. Choose an opaque identifier instead of personal details. |
| exp | Unix seconds, future expiry no more than five minutes ahead. |
| nonce | A fresh unique value of 16 to 128 URL-safe letters, digits, underscores or hyphens. |
import { createHmac, randomBytes } from 'node:crypto';
export function createViewerGrant(experienceId, viewerId, signingSecret) {
const payload = {
experience_id: experienceId,
viewer_id: viewerId,
exp: Math.floor(Date.now() / 1000) + 240,
nonce: randomBytes(16).toString('base64url'),
};
const encoded = Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url');
const signature = createHmac('sha256', signingSecret)
.update(encoded)
.digest('base64url');
return encoded + '.' + signature;
} The helper does not authenticate a viewer or authorize a video. Your server must do those checks before signing and must never return its signing secret.
Deliver and rotate
Deliver only a fresh grant to the authorized host page, then pass it through the wrapper’s grant option. The wrapper transmits it with validated host:init, never in the iframe URL.
Do not copy a real grant into a reusable snippet. Issue a new nonce for a new redemption. The shared production store claims each nonce once for the grant’s entire lifetime. Expired grants are refused. Do not reuse a grant.
The runtime handles access-token and refresh-token renewal after redemption. The same experience and stable viewer identifier resolve to the same identified viewer session.
Rotating the signing secret invalidates unredeemed grants signed with the previous secret. It does not independently erase existing viewer sessions. Use Revoke to disable the experience.