@hexdn/sdk
v0.4.0
Published
Trusted-server API client and playback helpers for HexDN.
Downloads
853
Readme
@hexdn/sdk
Typed HexDN API access and playback helpers for trusted servers. It has no runtime dependencies and works with standard Fetch and Web APIs in Node, Bun, and Workers. Keep the environment API key on your server; browser playback and uploads use their own scoped capabilities.
Before direct browser uploads, have your HexDN operator register your website for uploads once. Registration applies and verifies storage CORS automatically; there is no separate tenant storage configuration. Origin provisioning is currently an operator task, not a method on the public server SDK.
import { HexDN } from "@hexdn/sdk";
const hexdn = new HexDN({ apiKey: configuration.hexdnApiKey });
const asset = await hexdn.assets.create(
{
externalId: applicationVideo.id,
outputs: [
{ type: "playback", access: "protected", renditions: "adaptive" },
],
},
{ idempotencyKey: applicationUploadRequest.id },
);
// Your authorized application endpoint can return this scoped capability to
// @hexdn/upload. Attach asset.id and the requested output IDs to your own content.
return Response.json({ upload: asset.upload });Asset/task creation and output creation generate an idempotency key when one is omitted. Supply a stable application key when an operation must survive retries across application requests. The SDK never chooses hidden outputs or product policy.
For an application that manages upload preparation and part tickets on its
backend, use assets.createUpload instead of assembling a multipart plan or
calling create and prepare separately:
const { asset, upload, plan } = await hexdn.assets.createUpload(
{
externalId: applicationVideo.id,
outputs: [
{ type: "playback", access: "protected", renditions: "adaptive" },
],
},
sourceByteLength,
{ idempotencyKey: applicationUploadRequest.id, signal: request.signal },
);The helper validates the source size, prepares the scoped upload, and returns
the asset, upload capability, and multipart plan. A retry can also return an
already completed upload; inspect upload.status before scheduling transfers.
Keep the same application idempotency key across retries. For the direct
@hexdn/upload flow above, uploadSource already handles preparation,
reconciliation, transfer, and completion in the browser.
If your application reserves quota or creates durable records before contacting
HexDN, validate the input first with the same checks used by createUpload:
import { validateAssetUploadInput } from "@hexdn/sdk";
validateAssetUploadInput(assetInput, sourceByteLength);This pure function runs synchronously, makes no network requests, and throws
HexDNError for invalid source sizes or request fields. It requires no API key
and leaves the supplied input unchanged.
For protected playback, authorize the viewer in your endpoint, resolve the persisted playback ID, and return an SDK-ready source:
// Inside POST /api/videos/:videoId/playback in your server router:
const viewer = await application.requireViewer(request);
const video = await application.authorizeVideo(viewer, requestedVideoId);
const source = await hexdn.playback.createSource(
video.playbackId,
{},
{ signal: request.signal },
);
return Response.json(source, {
headers: { "Cache-Control": "private, no-store" },
});Pass that route to the React player as
sourceEndpoint="/api/videos/42/playback", or to createPlaybackSourceEndpoint
from @hexdn/playback. The browser receives only the authorized source
envelope. The application supplies its normal viewer authentication and video
lookup; those functions remain outside the SDK.
Viewer/session attribution and credential TTLs are optional advanced settings; ordinary playback uses the service defaults. Supply them only when your access policy needs them.
The helper validates playback/session URL scope and supplies the proof-header
configuration expected by @hexdn/playback. Use the same authorized endpoint
for renewal. Credentials and viewing attempts have separate lifecycles; this SDK
does not create analytics attempts. Public ready outputs already provide a
manifest URL and need no credential request. Lower-level
playback.createSession, playback.createToken, and playback.revokeSession
expose the documented control operations.
For signed playback, persist the ready output's signed playback result with your
video and pass it to createSignedSource. No manifest or token URL assembly is
needed:
// Inside POST /api/videos/:videoId/playback in your server router:
const viewer = await application.requireViewer(request);
const video = await application.authorizeVideo(viewer, requestedVideoId);
// video.playback is the SignedPlaybackResult from the ready playback output.
const source = await hexdn.playback.createSignedSource(
video.playback,
{},
{ signal: request.signal },
);
return Response.json(source, {
headers: { "Cache-Control": "private, no-store" },
});Use this same endpoint for acquisition and renewal. The helper validates the
playback/version scope, signs the manifest and optional preview URLs, and
returns expiresAt for the player to schedule renewal. Keep the persisted ready
descriptor current when HexDN returns a replacement output version. Credential
renewal preserves the playback identity and does not create an analytics
attempt.
Playback-origin restrictions are optional. If your environment has playback origins configured, include your application's origin; an empty playback-origin list does not restrict origins. Upload and analytics origins are configured separately.
Available resource groups: assets, tasks, outputs, uploads, playback,
webhookEndpoints, events, and analytics. Public API types are exported as
components, operations, and paths, generated from the maintained HexDN
OpenAPI contract and included in the published package.
Provision browser analytics once per origin configuration on your server:
const key = await hexdn.analytics.collectionKeys.create({
origins: ["https://app.example"],
});
const publicAnalytics = hexdn.analytics.collectionConfig(key.id);Use your application's exact origins (scheme, host and port), without wildcards.
Pass publicAnalytics to @hexdn/analytics or the React player's analytics
prop, adding a stable video identity when it is not already available. The
helper makes no requests and derives routing from this client. Production
browser integrations can also use just { collectionKey: key.id }. Keep the
environment API key private.
const metrics = await hexdn.analytics.metrics({
from: "2026-01-01", // Inclusive UTC day.
to: "2026-02-01", // Exclusive UTC day.
videoId: applicationVideo.id,
groupBy: "day",
});
const keys = await hexdn.analytics.collectionKeys.list();For a page of videos, pass videoIds once instead of requesting each video's
metrics separately:
const period = await hexdn.analytics.metrics({
from: "2026-08-01",
to: "2026-09-01",
videoIds: videos.map((video) => video.id),
});
const totals = await hexdn.analytics.totals({
videoIds: videos.map((video) => video.id),
});
// One video is also supported: hexdn.analytics.totals({ videoId }).Lists accept 1–100 identifiers and cannot be combined with videoId. Duplicates
are removed in first-requested order. Period batches imply video grouping,
reject day grouping and limits, and return one row per selected ID in that
order. Grouped period rows keep their identifier in value; totals use
videoId. Every row includes hasData: false means no accepted aggregate is
available, with zero counters and null timestamps. It does not establish zero
watching for a video that was not instrumented.
Period queries reject dates older than the rolling 13-calendar-month retention
window. Totals accumulate starts and playing_ms independently of period
retention, and accept no dates, grouping or limits. Their trackingSince is the
earliest included UTC activity or attempt-cohort day; it does not promise the
video's entire history was tracked or restore data pruned before totals began.
updatedAt is the latest aggregate write time, not an ingestion watermark.
Reads reflect reports after queue consumption, so collection is eventually
visible. These counters summarize accepted client measurements; they are not
authoritative proof of human watching or tenant-defined public views.
Analytics collection keys are browser-visible configuration; these management and query methods remain environment-authenticated. Analytics response types come from the maintained OpenAPI contract.
Verify and decode webhooks from the original request bytes:
import { verifyWebhookEvent } from "@hexdn/sdk";
const rawBody = new Uint8Array(await request.arrayBuffer());
const { event, deliveryId } = await verifyWebhookEvent({
headers: request.headers,
rawBody,
signingSecrets: configuration.hexdnWebhookSecrets,
});The helper verifies HMAC-SHA256 using WebCrypto, supports secret rotation, and
rejects timestamps more than 300 seconds in the past or future. It validates the
versioned envelope, required headers, and matching event, subject, root, and
data identities. The returned event is typed and accepts optional additive
fields. Previously queued output.playable deliveries retain their legacy event
type after validation; new events use the current contract.
Verification failures expose HexDNWebhookVerificationError.code and detail.
Your endpoint decides which events matter to your product. Atomically persist
event.id with application changes before acknowledging delivery; repeated
events must not apply those changes twice. Use deliveryId for delivery
diagnostics. verifyWebhookSignature remains available when only signature
verification is needed.
Requests default to a 15-second timeout per attempt and a 1 MiB JSON response
limit, including streamed bodies. GET and explicitly idempotent requests may
retry once on transient failure. Unkeyed credential creation and upload
completion are not replayed automatically: an uncertain completion must be
reconciled against authoritative state. Pass requestTimeoutMs and signal for
tighter operation budgets. HexDNError exposes code, status, retryable,
requestId, and available server details without attaching credentials.
Typed resource methods validate requests and responses against the maintained
API contract, including identity bindings. They reject obsolete request fields
before sending them. Application authorization and product validation remain
your responsibility. Advanced adapters can use
requestJson({ pathname, method, expectedStatuses, decode, ... }) for an
operation outside those methods, supplying their own response decoder.
For internal Lab integrations, pass env: "lab" to the constructor. Omit it for
production. This selects the HexDN service; the API key still identifies the
tenant environment. Browser analytics configuration inherits this selection.
pnpm --filter @hexdn/sdk build
pnpm --filter @hexdn/sdk check:generated
pnpm --filter @hexdn/sdk typecheck
pnpm --filter @hexdn/sdk testBuild synchronizes the public declaration source from the existing contracts workspace. Published code and declarations do not depend on that private package.
