Embed the Genuin Web SDK on your website
Render your brand, community, and group feeds on your own pages with Web SDK 2.0.6, from one script tag to several embeds, sign-in, page context, and events.
The Genuin Web SDK is a script that turns a <div> on your page into a video embed. You configure the embed in Brand Control Center, which feed it shows and how it's laid out, and the SDK fetches that configuration by its embed ID and renders it. Saving a change in Brand Control Center updates every page that shows the embed at its next load.
This page covers embeds, where you pick the feed once. A placement chooses its videos at load time from the page a visitor is on; see Add a Genuin placement to your website. Both use the same script and the same init call.
What the SDK renders
An embed combines a feed source with a style, both set in Brand Control Center under Grow > On Site > Embed.
| Part | Choices | What visitors see |
|---|---|---|
| Feed source | Brand Feed, Community Feed, Group Feed | Everything published across your brand, or the communities or groups you pick |
| Style | Standard Wall, Feed, Carousel | A grid of video tiles with navigation arrows, a vertical feed with a headline and button, or a horizontal row to swipe through |
| Expand view | On or off per embed | Tapping a tile opens the full-screen player with comments, reactions, and sharing over your page |
The SDK renders inside a Shadow DOM, so its styles never leak into your page and your CSS never reaches the player. It shows a skeleton at once, loads the player when the container comes within 200 px of the viewport, and sets the container's height to fit the content.
Requirements
| Requirement | Detail |
|---|---|
| Browser | ES2020 JavaScript with dynamic import(), Shadow DOM, and IntersectionObserver. Genuin tests Chrome 80, Firefox 75, Safari 13, and Edge 80 and later. |
| HTTPS | Every SDK asset and API endpoint is served over HTTPS only. Serve the host page over HTTPS as well. |
| Content Security Policy | If your page sets one, allow script-src and style-src for https://media.begenuin.com, connect-src for https://api.begenuin.com, https://media.begenuin.com, and https://rudderstack.begenuin.com, img-src and media-src for https://media.begenuin.com, style-src for https://fonts.googleapis.com, font-src for https://fonts.gstatic.com, and script-src for https://cdn.rudderlabs.com and https://cdn.jsdelivr.net. Brands that run ads add their ad vendors' hosts. |
| Container | A block element with a non-zero width and height. The SDK treats a container with zero width or height as a hidden expand-only host and renders nothing inline. |
| Credentials | An embed ID and your brand's API key, both from Brand Control Center. The key is a browser key and ships in your page source. |
| One brand per page | Every container on a page must use embeds of the same brand. Sign-in uses the first embed's brand for the whole page. |
Step 1: Copy the embed ID and API key
- In Brand Control Center, go to Grow > On Site and click Embed.
- On the embed's card, click the ⋯ menu and choose Copy Embed ID.
- Open the embed and click View Embed Code. The Web tab shows your API key in the generated
initcall. Copy the key from there.
To create the embed first, see Embed your community on your website.
Step 2: Add the container and the script
Add a container where the embed goes, then load the SDK from the versioned URL. The container needs an id of gen-sdk, an id that starts with gen-sdk-, or the class gen-sdk-class.
<div id="gen-sdk" class="gen-sdk-class" style="width: 100%; height: 600px;"></div>
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>The script is a 6 KB loader. It imports the SDK bundle and its stylesheet from the same /sdk/2.0.6/ directory and queues any init call you make before the bundle arrives.
Caution: Pin the version in the URL. The unversioned https://media.begenuin.com/sdk/gen_sdk.min.js serves the 1.2 line, which has a different API, and a floating URL changes behaviour without a deploy on your side.
Step 3: Initialize the embed
Call window.genuin.init once, after the script tag. You can call it before the bundle has loaded; the loader runs it when ready.
<script>
window.genuin.init({
embed_id: "{{EMBED_ID}}",
api_key: "<<YOUR_API_KEY>>",
});
</script>The SDK finds every container on the page, fetches your brand with the API key, fetches the embed, and renders it. A container's data-* attributes override the matching init value, which is how one init call serves several embeds.
Note: init scans the DOM when it runs. In a framework, call it after the container has rendered; the SDK skips containers it has already initialized, so calling it again is safe.
Step 4: Show several embeds on one page
Give each container a unique id, put its embed ID on the element, and call init once. api_key in init is the fallback for containers without their own data-api-key.
<div id="gen-sdk-1" class="gen-sdk-class" data-embed-id="{{EMBED_ID_1}}"
style="width: 100%; height: 600px;"></div>
<div id="gen-sdk-2" class="gen-sdk-class" data-embed-id="{{EMBED_ID_2}}"
style="width: 100%; height: 400px;"></div>
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>
<script>
window.genuin.init({ api_key: "<<YOUR_API_KEY>>" });
</script>Two containers with the same id log [Genuin SDK Warning]: Found 2 elements with the same ID and only the first initializes.
Step 5: Sign viewers in (Optional)
Visitors can watch without an account. To let them react, comment, and follow as themselves, pass an auto-login token that your server issued for the signed-in user. The SDK exchanges it for a Genuin session, creating the account on first use from params.
window.genuin.init({
embed_id: "{{EMBED_ID}}",
api_key: "<<YOUR_API_KEY>>",
token: "{{AUTO_LOGIN_TOKEN}}",
params: { name: "Ada Lovelace", email: "ada@example.com", nickname: "ada" },
});When the visitor signs in later, call update with the token; it signs them into every embed on the page. Call logout when they sign out of your site.
window.genuin.update({ container_id: "gen-sdk", token: "{{AUTO_LOGIN_TOKEN}}", user_params: {} });
window.genuin.logout();To run your own login screen when a signed-out visitor taps an action, define window.genuinAuth before init. The SDK calls it instead of showing its own sign-in, with the action the visitor attempted and returnQueryParams to restore it after login.
window.genuinAuth = ({ action, returnQueryParams }) => {
// Open your login flow, then hand the token to the SDK:
// window.genuin.update({ container_id: "gen-sdk", token: "{{AUTO_LOGIN_TOKEN}}", user_params: {} });
};An invalid or expired token leaves the visitor signed out; the SDK logs a warning and renders the embed anyway. How tokens are issued is covered in How Auto Login works and Choose how members sign in to the Genuin SDK.
Step 6: Pass page context (Optional)
An embed with no fixed community or group ranks its videos toward the page_context you send. Pass it in contextual_params, and change it with update when a single-page app changes route; the embed reloads with the merged context.
window.genuin.init({
embed_id: "{{EMBED_ID}}",
api_key: "<<YOUR_API_KEY>>",
contextual_params: {
page_context: "electric SUVs, charging, range",
geo: { lat: 37.7749, long: -122.4194 },
},
});
// Later, on a route change:
window.genuin.update({ container_id: "gen-sdk", contextual_params: { page_context: "hybrid sedans" } });On a container, the same values are data-page-context, data-lat, and data-long. The full list of signals and how they rank the feed are in Deliver contextual feeds with page, user, and location context and How contextual feeds choose videos.
Step 7: Open a specific video (Optional)
To land a visitor on one video, pass its slug. By default this opens the expand view on load; set data-expand-on-load="false" to play it inline instead.
<div id="gen-sdk" class="gen-sdk-class"
data-embed-id="{{EMBED_ID}}" data-api-key="<<YOUR_API_KEY>>"
data-start-video-slug="{{VIDEO_SLUG}}"
style="width: 100%; height: 600px;"></div>Add data-action="comment" to open the comment box as well, or data-comment-id to focus one comment. From code, window.genuin.expand("gen-sdk") and window.genuin.collapse("gen-sdk") open and close the expand view of a container.
Use the SDK in a framework
The SDK has no npm package; every framework loads the same script. The pattern is the same everywhere: render the container, load the script once, and call init after it loads.
React
Add a type declaration once so window.genuin compiles.
interface Window {
genuin?: {
init: (config: Record<string, unknown>) => Promise<void>;
update: (config: Record<string, unknown>) => Promise<void>;
on: (event: string, listener: (e: { type: string; payload: unknown }) => void) => Promise<() => void>;
logout: () => Promise<void>;
};
}Then load the script in an effect. The guard keeps React strict mode and re-mounts from adding it twice.
import { useEffect } from "react";
const SDK_URL = "https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js";
export function GenuinEmbed({ embedId }: { embedId: string }) {
useEffect(() => {
if (document.querySelector(`script[src="${SDK_URL}"]`)) {
window.genuin?.init({});
return;
}
const script = document.createElement("script");
script.src = SDK_URL;
script.async = true;
script.onload = () => window.genuin?.init({});
document.body.appendChild(script);
}, []);
return (
<div id="gen-sdk" className="gen-sdk-class" data-embed-id={embedId}
data-api-key="<<YOUR_API_KEY>>" style={{ width: "100%", height: 600 }} />
);
}Next.js
In the App Router, render the container and the script from a client component with next/script. afterInteractive loads the SDK after hydration, and onLoad runs init once the loader is on the page.
"use client";
import Script from "next/script";
export function GenuinEmbed({ embedId }: { embedId: string }) {
return (
<>
<div id="gen-sdk" className="gen-sdk-class" data-embed-id={embedId}
data-api-key="<<YOUR_API_KEY>>" style={{ width: "100%", height: 600 }} />
<Script
src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"
strategy="afterInteractive"
onLoad={() => window.genuin?.init({})}
/>
</>
);
}next/script loads a given src once per session, so onLoad doesn't fire again on client-side navigation. On pages reached that way, call window.genuin?.init({}) from a useEffect as well; already-initialized containers are skipped.
Angular
Inject the script in ngAfterViewInit, once the container exists. The component below is standalone and works in Angular 16 and later.
import { AfterViewInit, Component, Input } from "@angular/core";
@Component({
selector: "genuin-embed",
standalone: true,
template: `<div id="gen-sdk" class="gen-sdk-class" [attr.data-embed-id]="embedId"
data-api-key="<<YOUR_API_KEY>>" style="width: 100%; height: 600px"></div>`,
})
export class GenuinEmbedComponent implements AfterViewInit {
@Input({ required: true }) embedId!: string;
ngAfterViewInit(): void {
const src = "https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js";
const genuin = () => (window as any).genuin;
if (document.querySelector(`script[src="${src}"]`)) { genuin()?.init({}); return; }
const script = document.createElement("script");
script.src = src;
script.async = true;
script.onload = () => genuin()?.init({});
document.body.appendChild(script);
}
}Configuration reference
init options
Pass these to window.genuin.init. Where a data attribute exists, setting it on a container overrides the init value for that container.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| api_key | string | Yes | none | Your brand's API key. Data attribute: data-api-key. |
| embed_id | string | Yes, unless a placement is set | none | The embed to render. Data attribute: data-embed-id. |
| placement_id | string | No | none | With style_id, renders a placement instead of an embed. See Add a Genuin placement to your website. Data attribute: data-placement-id. |
| style_id | string | No | none | The placement style. Data attribute: data-style-id. |
| token | string | No | none | Auto-login token for the current visitor. Empty, "null", and "undefined" are ignored. Data attribute: data-token. |
| params | object | No | none | Profile for a new account created from token: name, email, mobile, nickname, profileImage (URL), brandUserIdentity. |
| contextual_params | object | No | none | Page and viewer context: page_context, previous_page_context, user_context, url, geo (lat, long, radius_limit), place, time, user_segments, user_interests, brands_ids, community_ids, loop_ids, posted_by_user_ids. Data attributes: data-page-context, data-previous-page-context, data-user-context, data-url, data-lat, data-long, data-geo, data-place, data-time, data-user-segments, data-user-interests, data-brands-ids, data-community-ids, data-loop-ids, data-posted-by-user-ids; objects and arrays as JSON strings. |
| configuration | object | No | none | Placement only. Overrides the style's style_title, style_subtitle, and sections. |
| start_video_slug | string | No | none | Video to open on load. Sets expand_on_load to true unless you set it to false. Data attribute: data-start-video-slug. |
| action | string | No | none | Interaction to perform on the start video: spark, comment-spark, repost, comment, report, join-community, join-group, subscribe-group, become-a-creator, octo. Data attribute: data-action. |
| comment_id | string | No | none | Comment to focus when the start video opens. Data attribute: data-comment-id. |
| auth_info | object | No | none | signInUrl and signUpUrl, each a string or a function returning one. Sign-in prompts send the visitor there instead of showing the SDK's own flow. |
| error_handler | function | No | none | Called with { isError, isNoContent } when an embed fails to load or its feed is empty. |
| theme | "light" or "dark" | No | "light" | Colour theme of the player. Data attribute: data-theme. |
| useShadowDOM | boolean | No | true | Set false to render into the page's DOM; the SDK's styles then share your cascade. |
| allow_gesture_scroll | boolean | No | true | Set false to turn off swipe and drag between tiles. Data attribute: data-allow-gesture-scroll. |
| video_ids | string | No | none | Comma-separated video ids that replace the feed. Data attribute: data-video-ids. |
| initial_video_ids | string | No | none | Comma-separated video ids to show first, ahead of the feed. Data attribute: data-initial-video-ids. |
| targeting | object | No | none | Ad and sponsorship targeting: keywords (string or array), location, page_url, and an open key_value map of up to 50 entries. Keywords and location are read from the page's <meta name="keywords"> and geo-IP when omitted. |
| sponsorship_id | string[] | No | none | Overrides the sponsorship attached to the embed. An empty array counts as absent. |
| expand_on_load | boolean | No | false | Data attribute only: data-expand-on-load="true" opens the expand view as soon as the embed loads. |
| embed_style | "expand_only" | No | none | Data attribute only: data-embed-style="expand_only" renders no inline player and serves the expand view alone. |
| brand_context, website_type, parent_instance_id, live | — | No | none | Partner and internal options for publisher integrations, preview mode, and nested SDK instances. Don't set them unless Genuin asks you to. |
contextualParams, authInfo, and errorHandler still work as aliases but are deprecated; use the snake-case names.
update options
Pass these to window.genuin.update after init. Calls made before the SDK is ready are queued.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| container_id | string | Yes | none | The id of the container to update. |
| token | string | No | none | Signs the visitor into every embed on the page. The TypeScript type lists it as required; an empty string is ignored. |
| user_params | object | No | none | Same keys as params. Listed as required by the type; pass {} when you have nothing to send. |
| contextual_params | object | No | none | Merged into the container's current context, then the feed reloads. |
| targeting | object | No | none | Replaces the container's targeting, so a field you leave out is cleared. |
| start_video_slug | string | No | none | Opens this video in the container's expand view. action and comment_id apply as in init. |
Methods
| Method | Returns | What it does |
|---|---|---|
| init(config) | Promise | Finds containers and renders them. Safe to call before the bundle has loaded. |
| update(config) | Promise | Changes a container's token, context, targeting, or start video. |
| on(event, listener) | Promise of an unsubscribe function | Subscribes to one of the events below. The listener receives { type, payload, timestamp }. |
| off(event, listener) | Promise | Removes a listener. |
| emit(event, payload) | Promise | Sends a command to the player: player:play, player:pause, player:mute, player:unmute, or sdk:themeChange. |
| expand(containerId) | Promise | Opens the container's expand view. |
| collapse(containerId) | Promise | Closes it. |
| logout() | Promise | Clears the signed-in visitor from every embed. |
| destroy() | void | Unmounts every embed and removes all listeners. Available once the bundle has loaded. |
| version, isLoaded(), isLoading() | string, boolean | The loader's version and bundle state. |
Events
Subscribe with on, which returns a function that unsubscribes. off(eventType, listener) removes a listener you kept a reference to. Names outside this list are rejected with a console warning.
window.genuin.on("onPlay", (event) => {
console.log(event.type, event.payload.muted, event.payload.isInView);
});| Event | Payload | Fires when |
|---|---|---|
| onFeedLoaded | videoCount, hasNextPage, isSectioned, feedType, thumbnailUrl, videoUrl | A feed page has loaded |
| onPlay | muted, isInView, isFocused, volume, autoplay | A video starts playing |
| onPause | muted, isInView, isFocused, volume | A video pauses |
| onMuteChange | muted, volume, instanceId | The visitor mutes or unmutes |
| onSwipedForward, onSwipedBackward | fromIndex, toIndex, timestamp | The visitor moves to another video |
| onVideoClicked | videoId | A tile is clicked |
| onExpandViewChanged | true or false | The expand view opens or closes |
| onShare | shareUrl, type, id, clipTitle, clipDescription, clipThumbnailUrl | The visitor shares a video |
| onVideoNotFound | slug, errorCode | A start_video_slug doesn't resolve |
| onAnalyticsTrack | eventName, eventPayload | The SDK records an analytics event, for forwarding to your own analytics |
| onFollowChanged, onCaughtOverlay, checkFollowingStatus, playIHeartContent | varies | Publisher-partner integrations; unused in a standard embed |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Nothing renders; the console shows Failed to fetch brand details | The API key is missing or wrong, or belongs to another environment | Copy the key from the Web tab of View Embed Code and pass it as api_key or data-api-key. |
| Console error Placement ID or Style ID is missing, and no embed ID provided | The container has no data-embed-id and init has no embed_id, or only one of placement_id and style_id was set | Add the embed ID, or both placement values. |
| [Genuin SDK Warning]: Found N elements with the same ID | Two containers share an id | Give each container a unique id, such as gen-sdk-1 and gen-sdk-2. |
| No valid SDK elements found for initialization. | init ran before the container existed, or the container lacks the gen-sdk id prefix and class | Render the container first, then call init. |
| window.genuin is undefined | The init script runs before the SDK script tag, or the script failed to load | Place the script tag before the init call and check the URL in the Network tab. |
| The player shows Unable to load content | The embed doesn't exist, the brand's web SDK is off, or a network error occurred | Check the embed ID in Brand Control Center and the failing request in the Network tab; error_handler receives isError: true. |
| The expand view opens but nothing renders inline | The container had zero width or height when init ran | Give the container a width and a height in CSS. |
| The visitor stays signed out after passing token | The auto-login request returned 401, or browser storage is blocked | Check that the token is current and issued for this brand. In a private window or inside a third-party iframe, the SDK keeps the session in memory only, so it ends with the page. |
| Scripts, styles, or requests are blocked | Your Content Security Policy doesn't allow a Genuin host | Add the hosts from the Requirements table. |
| SDK initialization is disabled via URL parameter. | The page URL contains disableGenSdk=true | Remove the parameter. |
Limitations
- CDN only: There's no npm package. Load
https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.jsand pin the version. - One brand per page: Sign-in resolves the brand from the first embed, so mixing brands on a page breaks authentication.
- Containers need a size: A zero-sized or hidden container is treated as an expand-only host.
- Shadow DOM: Your CSS doesn't style the player, and the player uses the Inter font loaded from Google Fonts. There's no font option in 2.0.6.
- Fixed event list:
onaccepts only the events listed above. Container resizing is handled internally and isn't subscribable. - Context merges, targeting replaces:
updatemergescontextual_paramsinto the existing context but replacestargetingwholesale. - Key-value targeting caps: 50 entries, keys up to 128 characters, values up to 1,024 characters, keywords up to 100 of 256 characters each,
page_urlup to 2,048 characters. Larger values are dropped with a console warning. - Lazy rendering: Embeds render when they come within 200 px of the viewport, except when a start video or expand-on-load is set.

