DRM quick start
Five calls take you from nothing to a metered licence. Everything below runs against the hosted service at https://api.superdrm.com; on an on-premises install swap in https://api.<your domain> (or http://127.0.0.1:8770 on the api host).
#1. Get a tenant
Hosted: sign up at superdrm.com/portal, verify your email, and copy the two secrets shown once: api_key (sdrm_live_…, for your servers) and token_secret (64 hex, for minting player licence tokens). Store both in your secret manager. See Portal & console.
On-premises: the installer creates a first tenant and writes its credentials to secrets/demo-tenant.json; create more with the CLI:
./bin/superdrm tenant:create acme "Acme Corp" --plan growth#2. Issue a content key
From the packager, one call per file. JSON:
curl -s -X POST https://api.superdrm.com/v1/keys \
-H "Authorization: Bearer sdrm_live_…" -H "Content-Type: application/json" \
-d '{"label":"acme-invoice-2026-09"}'const r = await fetch("https://api.superdrm.com/v1/keys", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SUPERDRM_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ label: "acme-invoice-2026-09" }),
});
const key = await r.json(); // { content_id, kid, key, iv, wv_pssh, fp_hls, … }{
"content_id": "6d8b28c0-605e-4531-b25c-1981e7b990fc",
"kid": "04304543e3ab52ff801fd4d366981781",
"key": "…32 hex…", "iv": "…32 hex…", "scheme": "cbcs",
"systems": ["widevine", "fairplay", "clearkey"],
"wv_pssh": "AAAATHBzc2g…", "pr_pssh": null,
"fp_hls": "URI=\"skd://6d8b28c0-…\",KEYFORMAT=\"com.apple.streamingkeydelivery\",KEYFORMATVERSIONS=\"1\"",
"licence_urls": { "com.widevine.alpha": "https://api.superdrm.com/v1/acme/license/widevine", "com.apple.fps": "…/fairplay", "org.w3.clearkey": "…/clearkey" },
"fairplay_cert_url": "https://api.superdrm.com/v1/acme/fairplay/cert",
"created": true
}Or CPIX 2 XML, the form packagers already parse:
curl -s "https://api.superdrm.com/v1/cpix?k=6d8b28c0-605e-4531-b25c-1981e7b990fc&c=acme-invoice&EncryptionScheme=cbcs" \
-H "Authorization: Bearer sdrm_live_…"Encrypt with the returned key, IV and PSSH (MP4Box, shaka-packager and Bento4 all accept CPIX or the raw values).
#3. Serve a licence
Two ways. Server relay — your backend authorises the viewer, then forwards the CDM challenge:
curl -s -X POST "https://api.superdrm.com/v1/acme/license/widevine?cid=6d8b28c0-605e-4531-b25c-1981e7b990fc" \
-H "Authorization: Bearer sdrm_live_…" \
-H 'X-SuperDRM-Policy: {"hw":true}' \
--data-binary @challenge.bin -o licence.bin -D - | grep -i x-superdrm-securityexport async function relay(system: string, contentId: string, challenge: Uint8Array, strict: boolean) {
const r = await fetch(`https://api.superdrm.com/v1/acme/license/${system}?cid=${contentId}`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SUPERDRM_KEY}`, "Content-Type": "application/octet-stream",
"X-SuperDRM-Policy": JSON.stringify({ hw: strict }) },
body: challenge,
});
if (!r.ok) throw new Error(`superdrm ${r.status}: ${await r.text()}`); // {error, code}
console.log("granted", r.headers.get("x-superdrm-security")); // L1 | L3 | hw | sw
return new Uint8Array(await r.arrayBuffer());
}Player token — your backend mints a token and the player talks to SuperDRM directly:
curl -s -X POST https://api.superdrm.com/v1/tokens \
-H "Authorization: Bearer sdrm_live_…" -H "Content-Type: application/json" \
-d '{"content_id":"6d8b28c0-…","sub":"user-42","ttl_s":3600,"policy":{"hw":true}}'const r = await fetch("https://api.superdrm.com/v1/tokens", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SUPERDRM_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ content_id: "6d8b28c0-…", sub: "user-42", ttl_s: 3600, policy: { hw: true } }),
});
const { servers, fairplay_cert_url } = await r.json();
player.configure({ drm: { servers } }); // Shaka: URLs already carry ?lt=<token>The reply's servers map drops straight into a Shaka drm.servers config; each URL already carries ?lt=<token>.
#4. Read the meter
curl -s "https://api.superdrm.com/v1/usage?from=2026-09-01&to=2026-09-30" -H "Authorization: Bearer sdrm_live_…"
curl -s "https://api.superdrm.com/v1/usage/licences?limit=20" -H "Authorization: Bearer sdrm_live_…"const h = { Authorization: `Bearer ${process.env.SUPERDRM_KEY}` };
const usage = await (await fetch("https://api.superdrm.com/v1/usage?from=2026-09-01&to=2026-09-30", { headers: h })).json();
const recent = await (await fetch("https://api.superdrm.com/v1/usage/licences?limit=20", { headers: h })).json();
console.log(usage.totals, recent.licences[0]); // { widevine: { ok, failed }, … }Every licence attempt is a row, refused ones included, with the security level actually granted (L1, L3, hw, sw) and an error code when refused.
#5. Revoke
curl -s -X POST https://api.superdrm.com/v1/keys/6d8b28c0-…/revoke -H "Authorization: Bearer sdrm_live_…"Every later licence for that content id is refused with revoked; the refusal is metered too. Token expiry (30 days at most) and the authorization webhook are the other two refusal paths.
#What to read next
- Migrating from another DRM vendor if you are replacing an existing relay.
- API reference for every route, field and error code.
- Licensing checklist for the partner paperwork that turns staging into production.
Updated September 2026