Developer getting-started guide
Everything needed to put ReCut videos, watch tracking, training reports and the editor into your site. Code samples are minimal and meant to be adapted.
Step 1: Create a workspace API key
Sign in as a workspace owner or admin, open Settings and find API keys. Create a key and name it after the system using it. The full key (starting rck_live_) is shown once — store it in your server's secret store.
All API calls are server to server. Browsers cannot call the API directly, and the key must never reach a web page.
# Store the key in your server's environment, never in browser code
RECUT_API_KEY=rck_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxStep 2: Mint a viewing pass
When a signed-in user opens a page with a video, your server asks for a pass. Name the hosts allowed to show it (up to 10, wildcards like *.example.com allowed) and a lifetime between 60 seconds and 24 hours (default 1 hour). The response includes a ready embed link.
Optionally add brand (accent, theme, logo_url, name — the same options as the editor pass) to match your site. The branding is sealed into the embed link and only works for that video; editing it in the page has no effect.
// Node 18+ (server only)
const res = await fetch("https://recut.stream/api/public/v1/playback-tokens", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RECUT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
video_slug: "onboarding-policy",
allowed_domains: ["hr.example.com", "*.staging.example.com"],
ttl_seconds: 3600,
brand: { accent: "#0a84ff", theme: "dark", logo_url: "https://hr.example.com/logo.png", name: "Acme HR" },
}),
});
const { token, embed_url, expires_at } = await res.json();Step 3: Show the player
Put the embed link in an iframe. Add &ref= with your own viewer reference (letters, numbers and _ . : @ - up to 128 characters) so watch updates come back tagged with it.
<iframe
id="recut-player"
src="EMBED_URL&ref=emp_1234"
width="960" height="540"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen
title="Training video"></iframe>Step 4: Receive watch updates
Load the helper script and listen. Events are ready, play, pause, seek, progress (milestone 25, 50, 75 or 90), heartbeat (every 10 seconds of real watching) and complete (at the end or 95%). Each carries time, duration, percent and watched seconds, where skipping doesn't count. Send them to your own server to store.
<script src="https://recut.stream/api/public/v1/embed-sdk.js"></script>
<script>
const frame = document.getElementById("recut-player");
const stop = ReCut.onWatch(frame, (msg) => {
// msg: { event, slug, ref, time, duration, percent, watched, milestone? }
navigator.sendBeacon("/my-api/watch-events", JSON.stringify(msg));
});
</script>Step 5: Send training reports (xAPI)
The helper turns each update into an xAPI statement with your viewer reference as the actor. Heartbeats are skipped. Post statements from your server to your training system's statement endpoint, so its credentials stay private.
ReCut.onWatch(frame, (msg) => {
const statement = ReCut.toXapi(msg, {
homePage: "https://hr.example.com",
activityBase: "https://hr.example.com/videos",
title: "Onboarding policy",
});
if (statement) fetch("/my-api/xapi", { method: "POST", body: JSON.stringify(statement) });
});
// Your server then forwards it to the LMS statement endpoint (POST {LRS}/statements)Step 6: List and look up videos
Search your workspace with q, status (published or draft), visibility, limit (up to 100, default 25) and offset. Look up a single video by its slug to get its approved release history.
const h = { Authorization: `Bearer ${process.env.RECUT_API_KEY}` };
const list = await fetch("https://recut.stream/api/public/v1/videos?q=safety&status=published&limit=25&offset=0", { headers: h }).then(r => r.json());
// { data: [...], total, limit, offset }
const one = await fetch("https://recut.stream/api/public/v1/videos/onboarding-policy", { headers: h }).then(r => r.json());Step 7: Mint an editor pass
Your server requests a pass for one project with scopes view, edit or submit (submit includes edit, edit includes view), the hosts allowed to frame it, an optional editor_ref for audit logs, and a lifetime between 5 minutes and 8 hours (default 2 hours).
Optionally add brand to match your site: accent (a hex colour like #0a84ff), theme (light or dark), logo_url (an https image) and name (up to 80 characters). Button text colour is picked automatically for readable contrast. Branding is sealed inside the pass, so nobody can change it from the page; an invalid colour or logo returns invalid_brand.
const res = await fetch("https://recut.stream/api/public/v1/editor-tokens", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.RECUT_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
project_id: "00000000-0000-4000-8000-000000000000",
scopes: ["submit"],
allowed_domains: ["hr.example.com"],
editor_ref: "emp_1234",
ttl_seconds: 7200,
brand: { accent: "#0a84ff", theme: "dark", logo_url: "https://hr.example.com/logo.png", name: "Acme HR" },
}),
});
const { token, scopes, expires_at } = await res.json();Step 8: Open the embedded editor
Frame the editor with the pass in the et parameter. The page must be opened from an allowed host. Your page can tell the editor to save or submit, and receives ready, dirty, saved, submitted and error events. Only messages from the framing page are accepted.
<iframe id="recut-editor"
src="https://recut.stream/embed-editor/PROJECT_ID?et=EDITOR_TOKEN"
width="100%" height="800" title="Video editor"></iframe>
<script>
const ed = document.getElementById("recut-editor");
const ORIGIN = "https://recut.stream";
window.addEventListener("message", (e) => {
if (e.origin !== ORIGIN || e.source !== ed.contentWindow) return;
if (e.data?.source !== "recut:editor") return;
console.log(e.data.type, e.data); // ready | dirty | saved | submitted | error
});
function save() { ed.contentWindow.postMessage({ source: "recut:host", v: 1, type: "save" }, ORIGIN); }
function submit() {
ed.contentWindow.postMessage({ source: "recut:host", v: 1, type: "submit",
releaseTitle: "Q4 update", releaseNotes: "New policy section" }, ORIGIN);
}
</script>Launch checklist
Key stored server-side only. Allowed hosts match your real domains, including staging. Videos set to the right visibility. Your page checks event.origin and the source field before trusting messages. A workspace owner or admin is ready to approve submissions from the embedded editor.