VideohatiDocs
Guides

Guide: Embed on any site (plain HTML, WordPress, any CMS)

Play a Videohati video on a plain HTML page, in WordPress, or in any CMS by pasting one script tag and a mount element.

You do not need a framework to show a Videohati video. There are two ways to embed, and which one you use depends on the video's visibility:

  • Public or unlisted videos — paste an <iframe> (or just share a link). No server code at all. Start with the next section.
  • Private videos — your server mints a short-lived session token per viewer, and the player mounts from a script tag. Your API key stays on the server the whole time. Start at Create a playback session.

Zero-code embed: public and unlisted videos

Every video starts private. Switch it to unlisted (anyone with the link can watch, not indexed by search engines) or public from the video's Share & embed card in the dashboard, or over the API:

curl -X PATCH https://api.videohati.com/v1/videos/01JZ9WV3N8GQ5T2M7K4C6XBARH \
  -H "Authorization: Bearer $VIDEOHATI_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "visibility": "unlisted" }'

Once the video is unlisted or public, three zero-code options open up:

Share link. Send anyone the hosted watch page — a branded, right-to-left page with the player ready to play:

https://cdn.videohati.com/watch/01JZ9WV3N8GQ5T2M7K4C6XBARH

Iframe. Paste this where the video should appear (the wrapper keeps a 16:9 shape at any width):

<div style="position:relative;padding-top:56.25%;">
  <iframe
    src="https://cdn.videohati.com/embed/01JZ9WV3N8GQ5T2M7K4C6XBARH"
    style="position:absolute;inset:0;width:100%;height:100%;border:0;"
    allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
    allowfullscreen loading="lazy" title="Videohati"></iframe>
</div>

Append ?lang=en to the /watch or /embed URL for an English player; Arabic is the default. Append autoplay=1 to the /embed URL to start muted on load.

oEmbed. Platforms that support oEmbed discovery (WordPress, Notion, and most LMSes) unfurl the watch link into a player on paste — no markup needed. The endpoint is https://cdn.videohati.com/oembed?url=<watch-url>.

Playback through all three still runs the full protection pipeline — signed media URLs, session heartbeats, and your project's geographic policy. The only thing that changed is who may open a session: anyone, for this one video.

For videos that must stay private — paid course content, internal material — keep reading: the token flow below leaves your server in control of every viewer.

1. Create a playback session on your server

A playback session is a short-lived permission to watch one video. You mint it with your API key, so this step must run on your server — never in the browser.

Your API key grants write access to the project. Never put a vh_live_ or vh_test_ key in a web page, a theme file, or client-side JavaScript.

curl -X POST https://api.videohati.com/v1/playback/sessions \
  -H "Authorization: Bearer $VIDEOHATI_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "videoId": "01JZ9WV3N8GQ5T2M7K4C6XBARH", "viewerDisplayText": "Ahmed K." }'

The response carries the sessionToken you pass to the page:

{
  "sessionId": "01JZ9WV3N8GQ5T2M7K4C6XBARK",
  "sessionToken": "01JZ9WV3N8GQ5T2M7K4C6XBARK.1781485200.bXktbWFj",
  "manifestUrl": "https://cdn.videohati.com/…/master.m3u8",
  "expiresAt": "2026-07-05T12:00:00Z"
}
import { Videohati } from "@videohati/node";

const client = new Videohati({ apiKey: process.env.VIDEOHATI_API_KEY });

const session = await client.playback.createSession({
  videoId: "01JZ9WV3N8GQ5T2M7K4C6XBARH",
  viewerDisplayText: "Ahmed K.", // shown in the watermark
});

// Send session.sessionToken to the page. Keep the API key on the server.
console.log(session.sessionToken);

2. Paste the embed on your page

Add the mount element and the script tag where the video should appear. The script scans the page for data-videohati-video-id and mounts a player on each match.

<div
  data-videohati-video-id="01JZ9WV3N8GQ5T2M7K4C6XBARH"
  data-videohati-project-id="01JZ9WV3N8GQ5T2M7K4C6XBARP"
  data-videohati-session-token="{{ session token from your server }}"
  data-videohati-lang="ar"
></div>
<script src="https://cdn.videohati.com/player.js" async></script>

Replace the placeholder in data-videohati-session-token with the token your server just created. The player refuses to start without one and shows a "Missing session token" message instead.

The data attributes

Set these on the mount element. Only the first three are required.

AttributeRequiredValuesWhat it does
data-videohati-video-idYes01JZ9WV3N8GQ5T2M7K4C6XBARHThe video to play.
data-videohati-project-idYes01JZ9WV3N8GQ5T2M7K4C6XBARPThe project the video belongs to.
data-videohati-session-tokenYestoken stringThe short-lived token from your server.
data-videohati-autoplayNo"true" | "muted"Start on load. Most browsers only allow autoplay when muted.
data-videohati-widthNonumberPlayer width in pixels.
data-videohati-heightNonumberPlayer height in pixels.
data-videohati-langNo"ar" | "en"Player interface language. Defaults to ar.
data-videohati-viewer-textNoany textText shown in the on-video watermark.
data-videohati-api-originNoURLPoint the player at a non-production API, such as staging.

WordPress and other CMSs

In WordPress, add a Custom HTML block to your post or page, then paste the mount element and the script tag from step 2. Any CMS that accepts raw HTML works the same way.

The one part a CMS cannot do for you is mint the session token — that needs your API key, which must stay on a server. Add a small endpoint on your site that calls playback.createSession and returns the token, then have your page fetch it before mounting the player. To create sessions automatically as videos become ready, see the webhooks guide.

Content Security Policy

If your site sends a Content Security Policy header, allow the two Videohati origins so the script can load and reach the API:

script-src https://cdn.videohati.com;
connect-src https://api.videohati.com https://cdn.videohati.com;
media-src blob:;