Kanorio Elements

The same protected players, PDF viewer, lightbox, curriculum and customer login that power hosted Kanorio sites, packaged for sites you build yourself. Signed URLs, resume, progress reporting and future upgrades (HLS, watermarks, captions) come for free.

Install

Install @kanorio/sdk and @kanorio/elements from npm, or load the CDN bundle with a single script tag. Every SDK helper is a thin wrapper over standard HTTP endpoints—pure fetch / curl works everywhere without the SDK.

Install & CLI
npm install @kanorio/sdk @kanorio/elements
npx @kanorio/cli types --site https://s-xxx.kanorio.com --out src/kanorio.d.ts
npx @kanorio/cli doctor --site https://s-xxx.kanorio.com
Web Components (HTML / CDN)
<link rel="stylesheet" href="https://cdn.kanorio.com/elements/v1/elements.css">
<script type="module" src="https://cdn.kanorio.com/elements/v1/kanorio-elements.js"></script>

<kanorio-analytics site-url="https://s-xxx.kanorio.com"></kanorio-analytics>
<kanorio-login site-url="https://s-xxx.kanorio.com" site-name="Demo"></kanorio-login>
<kanorio-curriculum site-url="https://s-xxx.kanorio.com" product="react-course"></kanorio-curriculum>
<kanorio-unit site-url="https://s-xxx.kanorio.com" deliverable="react-course"></kanorio-unit>
React / Next.js
import { KanorioProvider, KanorioAnalytics, KanorioLogin, KanorioUnit } from "@kanorio/elements";
import { createDeliveryClient, createRevalidateHandler, seoRewrites } from "@kanorio/sdk";
import "@kanorio/elements/elements.css";

// 1. next.config.ts: rewrite /sitemap.xml, /robots.txt, /llms.txt, /feed.xml -> Kanorio Delivery API
// rewrites: async () => seoRewrites({ siteUrl: process.env.KANORIO_SITE_URL!, framework: "next" })

// 2. app/api/revalidate/route.ts: verify the X-Kanorio-Signature (t=…,v1=…) webhook header & call revalidateTag(cacheTags)
// export const POST = createRevalidateHandler({ signingSecret: process.env.KANORIO_WEBHOOK_SECRET!, revalidateTag });

// 3. Pure REST fallback (no SDK required):
// curl https://s-xxx.kanorio.com/api/v1/collections/blog/items?expand=authors,categories
// curl https://s-xxx.kanorio.com/api/v1/seo/jsonld?path=/about

// 4. <KanorioAnalytics /> reports every client-side navigation (pushState / replaceState / popstate) automatically.
//    Add your storefront origin to Settings → Developer → Allowed origins, or the browser will block the beacon.
<KanorioProvider siteUrl="https://s-xxx.kanorio.com" locale="en" theme="auto">
  <KanorioAnalytics />
  <KanorioLogin siteName="Demo" />
  <KanorioUnit deliverable="react-course" onProgress={(e) => console.log(e)} />
</KanorioProvider>

Elements

  • <kanorio-login> — magic-link / 6-digit code sign-in. Stores the customer Bearer in localStorage["kanorio.customer.token"] and fires kanorio:login.
  • <kanorio-unit deliverable="…"> — the protected player: outline sidebar, video / audio / PDF / text / file / embed units, locked and not-yet-released states, automatic progress reporting. Fires kanorio:unit-change and kanorio:progress.
  • <kanorio-curriculum product="…"> — public outline for a product (chapters, units, durations, free previews playable anonymously). No login required.
  • <kanorio-media src="…" mime-type="…"> — public media player for a URL.
  • <kanorio-lightbox images='["…"]'> — image lightbox with zoom, keyboard and download / share controls governed by the site's media protection.
  • <kanorio-form form="…"> — interactive form with client validation, anti-spam challenge token, private file upload tickets, and submission handling.
  • <kanorio-analytics> — privacy-first pageview and SPA route change tracker (pushState / replaceState / popstate) using non-blocking sendBeacon.

Common attributes: site-url (defaults to the current origin), locale (defaults to <html lang>; 58 locales built in), theme (dark / light / auto), token. Styling uses --media-*, --site-* and --brand-color CSS variables with sensible defaults. Vidstack and pdf.js load on demand.

Capabilities with drop-in Elements

Full matrix
For the full 12-domain capability & headless support matrix, see the Capabilities page.
CapabilityElementsAPI + SDKHosted
Customer login (magic link / 6-digit code)<kanorio-login> (stores Bearer in localStorage, kanorio:login event)Customer POST /auth/challenge, POST /auth/verify → Bearer; GET /me; DELETE /session/account/login
Entitlements (who may access what)<kanorio-unit> shows locked / preview / not-released statesAgent entitlements.grant / revoke / list / check; Customer GET /access/:idBackoffice contact → Access tab; /data/entitlements; /account/content
Forms & lead capture<KanorioForm /> (<kanorio-form>), <KanorioBotProtection />, useKanorioBotProtection()Agent forms.list/get, submissions.list/get; Delivery GET /forms/:id, GET /forms/:id/challenge, POST /forms/:id/upload-request, POST /forms/:id/submit; SDK forms.client({ siteUrl })/marketing/forms, /data/form_submissions; page form section
Deliverables (courses, downloads)<kanorio-curriculum> (public outline, free previews)Agent deliverables.*, deliverable_units.*; Delivery GET /products/:slug outline; Customer GET /deliverables, GET /deliverables/:key/store/deliverables, /data/deliverables, /data/deliverable_units; product content-curriculum section
Protected playback & downloads<kanorio-unit> (video, audio, PDF, text, files, embeds)Customer POST /units/:id/open (600 s signed URL); Delivery POST /units/:id/open for previews/account/content/%3Akey
Progress & completion<kanorio-unit> reports progress automatically; kanorio:progress eventCustomer POST /progress, GET /deliverables/:key/progress; Agent progress.list/data/progress; /account/content progress bars
Site team RBAC (custom roles, access requests), Agent PAT / OAuth 2.1 & Customer Session Bearer<kanorio-login>PAT (knr_pat_), OAuth 2.1 (knr_at_) with site scope; auth.whoami?websiteId= reports role vs. token scope; Customer Bearer (knr_cat_) bound to (websiteId, contactId)/settings/team, /settings/team/roles, /account/api-access
Public media, responsive images, LQIP placeholders & lightbox<kanorio-media src mime-type>, <kanorio-lightbox images='[…]'> (reads the site's media protection when protection is omitted)Agent media.* (images, files, protected deliverable files, media.update focus & alt text); Delivery assets map (url, width, height, alt, focus, watermarked, placeholder 16px WebP data URL); GET /api/v1/site mediaProtection/media library; gallery & media sections
First-party visitor & AI crawler traffic analytics (`/analytics`)<KanorioAnalytics />Delivery POST /analytics/pageview (anonymous, per-IP rate-limited, records path / referrer / UA category); Agent analytics.*/analytics

Versioning

Elements and the SDK share a major version and follow the API stability policy (opens in a new tab): attributes and props are only added, never changed or removed within a major. Pin the CDN URL to a major (/elements/v1/) and add Subresource Integrity when you self-host.

@kanorio/sdk Subpath Modules (Preview, Forms, Edge & Revalidate)

Every subpath in @kanorio/sdk is runtime-agnostic (Node, Cloudflare Workers, Vercel Edge, Deno, Bun, and browsers).
@kanorio/sdk/preview & @kanorio/sdk/forms
// 1. Next.js Draft Mode & Astro preview route handler (@kanorio/sdk/preview)
// app/api/draft/route.ts — verifies knr_pv_ token format & signature against Delivery API,
// blocks open redirects, sets HttpOnly "kanorio_preview" cookie, and 307 redirects.
import { draftMode } from "next/headers";
import { createPreviewRouteHandler, tokenFromCookieHeader, previewHeaders } from "@kanorio/sdk/preview";

export const GET = createPreviewRouteHandler({
  siteUrl: process.env.NEXT_PUBLIC_KANORIO_SITE_URL,
  enableDraftMode: async () => (await draftMode()).enable(),
});

// In a Server Component when reading drafts:
// const token = tokenFromCookieHeader( requestHeaders.get("cookie") ) ?? process.env.KANORIO_PREVIEW_TOKEN;
// fetch(url, { headers: previewHeaders(token!), cache: "no-store" });

// 2. Custom headless form UI helpers (@kanorio/sdk/forms)
import { createFormsHelpers } from "@kanorio/sdk/forms";

const formsApi = createFormsHelpers({ siteUrl: "https://s-xxx.kanorio.com" });
const schema = await formsApi.get("form_contact");
const challenge = await formsApi.challenge("form_contact");
// Optional private file upload ticket: await formsApi.requestUpload("form_contact", { fieldId, fileName, mimeType, fileSize });
const result = await formsApi.submit("form_contact", {
  answers: { name: "Alex", email: "alex@example.com" },
  protection: challenge,
  consent: "checked",
});

// 3. Edge crawler firewall, 301/302 redirects & bot-hit telemetry (@kanorio/sdk/edge)
// proxy.ts (Next.js 16; on Next.js 15 name it middleware.ts and export `middleware`)
// Syncs crawlerRules.blockedTokens and /api/v1/redirects every 60s. Redirects match exact paths only.
import type { NextFetchEvent, NextRequest } from "next/server";
import { createKanorioMiddleware } from "@kanorio/sdk/edge";

const kanorioEdge = createKanorioMiddleware({
  siteUrl: process.env.NEXT_PUBLIC_KANORIO_SITE_URL!,
});

export function proxy(request: NextRequest, event: NextFetchEvent) {
  return kanorioEdge.handleRequest(request, event);
}

// Skip build assets so crawler fetches of JS/CSS are not reported as bot hits
export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};

@kanorio/cli Command Reference

Command-line wrapper around the Kanorio Agent REST API and Delivery schema discovery. Authenticate via kanorio login --token knr_pat_... or KANORIO_TOKEN.
npx @kanorio/cli <command>
# Types & External Storefront Health (no PAT required for public types)
npx @kanorio/cli types --site https://s-xxx.kanorio.com --out src/kanorio.d.ts
npx @kanorio/cli doctor --site <siteId> [--switching]

# Auth & Sites
npx @kanorio/cli login --token knr_pat_...
npx @kanorio/cli sites list | sites get <siteId>
npx @kanorio/cli sites create --name "Sunny Bakery" --locale en [--brief brief.json]
npx @kanorio/cli jobs get <jobId>

# Theme Settings, Globals (Header / Footer) & Pages
npx @kanorio/cli settings get <siteId> | settings update <siteId> --brand-color "#2563eb"
npx @kanorio/cli globals get <siteId> | globals replace-header <siteId> --file header.json
npx @kanorio/cli pages list <siteId> | pages get <siteId> <pageId>
npx @kanorio/cli pages create <siteId> --title "About" --slug /about
npx @kanorio/cli pages replace-sections <siteId> <pageId> --file sections.json
npx @kanorio/cli sections describe [type...] [--detail full]

# Publish, Custom Domains & Media
npx @kanorio/cli publish <siteId> [--subdomain sunny-bakery] | unpublish <siteId>
npx @kanorio/cli domains list <siteId> | domains status <siteId> | domains bind <siteId> example.com
npx @kanorio/cli media list <siteId> | media upload <siteId> --file ./hero.jpg