npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@interhumanai/sdk

v0.6.0

Published

Official TypeScript SDK for the Interhuman API (auth, upload analysis, live stream analysis, and real-time multi-track analysis).

Readme

Interhuman TypeScript SDK

Official TypeScript/JavaScript SDK for the Interhuman API. It wraps the public API surfaces behind a small, typed client:

  • Auth — exchange API-key credentials for a short-lived access token (POST /v1/auth), or mint/revoke capped client tokens for direct browser use (POST /v1/client_tokens).
  • Upload — analyze a complete video file (POST /v1/upload/analyze).
  • Stream — analyze a live feed over a WebSocket (WS /v1/stream/analyze).
  • Real-Time — analyze a live feed with multi-track analysis and periodic feedback (WS /v0/realtime/analyze).

The SDK handles the token exchange, multipart upload, and the WebSocket envelope protocol for you, so you give it your API key once and make a single call per surface.

This SDK targets the API exactly as it ships today. The real-time endpoint is a temporary v0 release: it lives under the /v0 path and accepts either the stream or the realtime scope. Both aspects may change when it graduates to v1.

Installation

npm install @interhumanai/sdk

The package ships both ESM and CommonJS builds with TypeScript declarations.

Supported runtimes

| Surface | Requirement | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Auth, Upload | A global fetch, FormData, and BlobNode.js ≥ 18 or any modern browser. On older Node, pass a fetch implementation via the fetch option. | | Stream, Real-Time | A WebSocket. Node.js ≥ 22 (global WebSocket) or any modern browser. On older Node, pass a webSocket factory (e.g. backed by the ws package). |

The SDK has no runtime dependencies.

Quickstart

import { InterhumanClient, IncludeFlag } from "@interhumanai/sdk";

const client = new InterhumanClient({
  credentials: {
    keyId: process.env.INTERHUMAN_KEY_ID!,
    keySecret: process.env.INTERHUMAN_KEY_SECRET!,
  },
  // environment: "staging", // defaults to "production"
});

// Upload a finished file
const report = await client.upload.analyze({
  file: { data: bytes, filename: "clip.mp4", contentType: "video/mp4" },
  include: [IncludeFlag.ConversationQualityOverall],
});
console.log(report.signals, report.conversation_quality?.overall);

InterhumanClient exchanges your credentials at /v1/auth and refreshes the resulting token automatically before it expires. If you already have a bearer token — including a direct API key, which the API accepts as a bearer token on every request — pass accessToken instead of credentials:

const client = new InterhumanClient({ accessToken: process.env.IH_API_KEY! });

Authentication

Get an API key (keyId + keySecret) from the Interhuman customer platform. You can let the client manage tokens for you (recommended), or mint one yourself:

import { AuthClient, Scope } from "@interhumanai/sdk";

const auth = new AuthClient();
const token = await auth.createToken({
  keyId,
  keySecret,
  scopes: [Scope.Upload, Scope.Stream],
});
// token.access_token, token.expires_in (≈900s), token.scope

Scopes:

  • Scope.Upload (interhumanai.upload) — required for upload.
  • Scope.Stream (interhumanai.stream) — required for stream. For the current temporary real-time release it also grants access to real-time.
  • Scope.Realtime (interhumanai.realtime) — the dedicated real-time scope (accepted by real-time alongside Scope.Stream for this release).

Client tokens (browser use)

To let a browser call the upload, stream, or real-time API without shipping your API key, mint a short-lived, capped client token on your backend and hand only that token to the browser:

const token = await auth.createClientToken({
  apiKey: process.env.INTERHUMAN_API_KEY!,
  scopes: [Scope.Stream], // optional; defaults to stream
  expiresIn: 300, // optional; clamped to 60–3600s
  maxConcurrent: 1, // one session at a time (the default)
  maxVideoSeconds: 600, // total video budget across upload/stream/real-time
  allowedOrigins: ["https://app.example.com"],
});
// token.access_token — safe to hand to the browser; the API enforces the caps.

The token grants the requested scopes (default stream), lives 60–3600s, and carries per-token caps (duration, video bytes, concurrency, video-second budget, origin allow-list) that the API enforces across upload, stream, and real-time (the video budget spans all three). Cut a token off early by revoking it — after which it is rejected on new requests and any live session it opened is closed:

await auth.revokeClientToken({
  apiKey: process.env.INTERHUMAN_API_KEY!,
  token: token.access_token,
});

There is no "extend a token" call — mint a new one when a token nears expiry or its video budget runs low.

Upload API

const report = await client.upload.analyze({
  file: { data: bytes, filename: "clip.mp4", contentType: "video/mp4" },
  include: ["conversation_quality_overall", "conversation_quality_timeline"],
  goalDimensions: ["clarity", "energy"], // triggers interaction feedback
  // conversationContext: "Sales discovery call", // alternative feedback driver
});
  • file accepts a Blob/File or { data, filename?, contentType? } where data is a Uint8Array, ArrayBuffer, or Blob.
  • Formats: mp4, avi, mov, mkv, mpeg-ts, webm. The clip must be ≥ 3 seconds and the upload ≤ 32 MB.
  • The response is an AnalysisResult with signals, engagement_state, and optional conversation_quality / feedback.

Stream API

const stream = client.stream();

stream.on("signal.detected", (e) => console.log(e.data.signal_type, e.data.start));
stream.on("signal.updated", (e) => console.log(e.data.signal_type, e.data.probability));
stream.on("signal.ended", (e) => console.log(e.data.signal_type, e.data.end));
stream.on("engagement.updated", (e) => console.log(e.data.state));
stream.on("conversation_quality.updated", (e) => console.log(e.data.overall));
stream.on("coverage.dropped", (e) => console.warn(e.data.ranges));
stream.on("error", (e) => console.error(e.data.code, e.data.message));
stream.on("session.ended", (e) => console.log("analysis closed:", e.data.reason));
stream.on("close", (info) => console.log(info.code, info.reason));

await stream.connect();
await stream.waitForSessionReady();

// Optional per-session settings; the last config sent wins.
stream.updateConfig({ include: ["conversation_quality_overall"], goal_dimensions: ["clarity"] });

// Send WebM or fragmented-MP4 video chunks as they are recorded (each ≤ 32 MB).
stream.sendVideo(chunk); // Uint8Array | ArrayBuffer | Blob

// Graceful shutdown: the server drains the accepted video, ends still-active
// signals, sends session.ended, and closes the socket itself (code 1000).
stream.requestClose();
  • Authentication uses the Sec-WebSocket-Protocol: access_token, <token> subprotocol pair. This is the portable method that works in browsers (where the standard WebSocket constructor cannot set an Authorization header) and in Node.
  • Server→client frames are typed envelopes { type, timestamp, correlation_id, data }. on(type, …) is fully typed per event; on("message", …) receives every envelope as the StreamEvent union.
  • Client→server: video is sent as binary frames; session config as a JSON text frame.
  • session.ready (available via waitForSessionReady()) reports the session's limits: idle timeout (5 min default), max duration (1 hour default), and the min/max chunk size.
  • requestClose() starts the graceful shutdown handshake: the server replies with session.closing (its max_drain_seconds is the longest you should wait), rejects any further video, finishes analyzing what it already accepted, emits signal.ended for still-active signals, sends session.ended, and closes the socket. close() remains the immediate, non-draining teardown.

Limitations

  • The stream protocol accepts WebM and fragmented MP4 chunks — the two shapes browsers produce via MediaRecorder (video/webm on most browsers, video/mp4 on Safari). A server-side pipeline must encode to one of the two. Clips cut at arbitrary byte offsets mid-structure are only supported for WebM.
  • coverage.dropped is informational, not an error: under heavy load some media is skipped (and not billed) while the session continues.
  • An abruptly disconnected session does not emit a trailing signal.ended for still-active signals — treat close as ending them at the last emitted time. End the session with requestClose() to receive them explicitly.

Real-Time API

The Real-Time API is a sibling of the Stream API: same ingest (send video chunks, receive signal.* events), plus multi-track analysis, a transcript channel in both directions, and periodic written guidance (feedback). client.realtime() mirrors client.stream():

const realtime = client.realtime();

realtime.on("signal.detected", (e) => console.log(e.data.signal_type, e.data.start));
realtime.on("transcript.generated", (e) => console.log("live transcript:", e.data.text));
realtime.on("realtime_feedback.generated", (e) => console.log("guidance:", e.data.text));
realtime.on("error", (e) => console.error(e.data.code, e.data.message));

await realtime.connect();
const ready = await realtime.waitForSessionReady();
console.log(ready.data.supported_session_config_options.analysis_groups);

// Feedback is opt-in: the instructions are the switch that enables it.
realtime.updateConfig({
  analysis_groups: ["audio", "visual"], // subset of what session.ready advertises
  realtime_feedback_instructions: "Goal: help me close a sales call.",
  realtime_feedback_frequency: "medium", // high|medium|low = every 10/20/30s of analyzed video
});

// Optionally supply your own transcript; it feeds the feedback prompt.
realtime.sendTranscript([{ start: 0.4, end: 2.3, text: "Yeah, exactly.", speaker: 0 }]);

realtime.sendVideo(chunk); // same binary chunks as the stream client

// Graceful shutdown, same handshake as the stream client: the server drains
// the accepted video and any in-flight feedback, sends session.ended, and
// closes the socket itself (code 1000).
realtime.requestClose();

Differences from the stream client:

  • The session config takes analysis_groups / realtime_feedback_instructions / realtime_feedback_frequencynot the stream include flags — and the endpoint never emits engagement.updated or conversation_quality.updated.
  • Two extra server events: transcript.generated (the server's own live, de-overlapped transcript — concatenate the text fields to reconstruct it) and realtime_feedback.generated (the periodic guidance, or NO_GUIDANCE).
  • signal.* events do not require the video analysis group.
  • sendTranscript(segments) sends a transcript.updated frame; the most recent transcript replaces any prior one. Text frames — config, transcript, session.close — remain accepted while a graceful close drains, and a transcript sent during the drain still feeds feedback runs started by draining windows.
  • Temporary release: the route is WS /v0/realtime/analyze (not v1) and accepts tokens holding either Scope.Stream or Scope.Realtime. Expect the path and accepted scopes to change when the endpoint graduates to v1. The previous hyphenated spelling WS /v0/real-time/analyze still works as a temporary compatibility alias; new integrations should use WS /v0/realtime/analyze.

Errors

  • HTTP failures throw InterhumanApiError carrying status, errorId (e.g. "ih2001"), correlationId, link, and the raw body.
  • Misconfiguration throws InterhumanConfigError before any network call.
  • Stream error envelopes arrive on the "error" event; transport-level socket failures arrive on "socketError".

See the error handling reference.

Environments

new InterhumanClient({ credentials, environment: "production" }); // api.interhuman.ai (default)
new InterhumanClient({ credentials, environment: "staging" }); // staging-api.interhuman.ai
new InterhumanClient({ credentials, baseUrl: "http://localhost:8080" }); // local dev

Examples

Runnable examples live in examples/: auth.ts, client-tokens.ts, upload.ts, stream.ts, realtime.ts.

Development

npm install
npm run typecheck
npm test
npm run build      # emits dist/ (ESM + CJS + .d.ts)

The unit tests live in the repository's top-level test tree at tests/sdk/typescript/ (mirroring this package's path), while the runner config stays here — run them with npm test from this directory.

API reference docs

The reference documentation on docs.interhuman.ai is generated from this package's TSDoc comments — do not hand-write it. Edit the comments in src/, then regenerate:

npm run docs   # TypeDoc → Mintlify MDX in docs-dist/mintlify/

On every push to main that touches the SDK, the Sync SDK docs to Docs workflow regenerates this page and opens a PR against the interhuman-docs repository.

Releasing

This package is versioned independently from the API service using semantic versioning, but in lockstep with the Python SDK (interhumanai on PyPI): both always carry the same version and release together from a single workflow. Publishing is automatic on a version bump, gated on a successful deploy:

  1. Bump version in package.json and __version__ in sdk/python/src/interhumanai/_version.py to the same value, and add a CHANGELOG.md entry to each package.
  2. Merge to main.
  3. After the Deploy workflow succeeds, the SDK release workflow builds, tests, and publishes both packages. It is idempotent per registry — it publishes only when the version changes — and tags the release sdk-v<version>.

You can also publish manually by pushing an sdk-vX.Y.Z tag (must match both SDK versions) or via workflow_dispatch (with an optional dry run). The sdk-v* tag namespace is intentionally separate from the API service's automatic release tags so the two never collide.

Setup, the NPM_TOKEN secret, and token rotation (our policy re-mints the npm token about every 90 days) are documented in docs/sdk-release.md.

License

Apache-2.0