Skip to main content
Home›Docs›SDKs›Node.js SDK
SDKs

Node.js SDK

Full reference for the official perceive8 npm package, including analyses, speakers, alerts, and live streaming.

Node.js SDK

The perceive8 npm package is the official JavaScript and TypeScript client for the Perceive8 Audio Intelligence API. It targets Node.js 18+ and all modern browsers.


Table of Contents

  1. Installation
  2. Client Initialization
  3. Resources Overview
  4. Analyses
  5. Speakers
  6. Alerts
  7. Stream
  8. Pagination
  9. Error Handling
  10. TypeScript Models & Types

Installation

npm install perceive8
# or
yarn add perceive8
# or
pnpm add perceive8

The package ships as ESM and CommonJS with full TypeScript declarations.


Client Initialization

import { Perceive8Client } from "perceive8";

// Using an API key (recommended for server-side)
const client = new Perceive8Client({
  apiKey: "pk_live_xxxxxxxxxxxx",
});

// Using a Bearer JWT (for browser / user-scoped calls)
const client = new Perceive8Client({
  token: "<supabase-jwt>",
});

// Optional: override the base URL (e.g. for local development)
const client = new Perceive8Client({
  apiKey: "pk_live_xxxxxxxxxxxx",
  baseUrl: "http://localhost:8000",
});

Perceive8ClientOptions

Option Type Required Default Description
apiKey string one of — API key sent as X-API-Key header
token string one of — JWT token sent as Authorization: Bearer header
baseUrl string no https://api.perceive8.com Override the API base URL

You must supply either apiKey or token. Supplying neither throws an Error immediately.


Resources Overview

The client exposes four resource namespaces:

Property Type Description
client.analyses AnalysesResource Upload, fetch, list, delete audio analyses
client.speakers SpeakersResource Manage enrolled speaker profiles
client.alerts AlertsResource List, acknowledge, and stream real-time alerts
client.stream StreamResource Open a live WebSocket transcription session

Analyses

client.analyses.upload(file, filename, language?)

Upload an audio file to create a new analysis.

const { data: analysis } = await client.analyses.upload(
  new Blob([buffer], { type: "audio/mpeg" }),
  "interview.mp3",
  "en"          // ISO 639-1 language code, default "en"
);

console.log(analysis.id);     // "uuid"
console.log(analysis.status); // "pending"

Parameters

Parameter Type Default Description
file Blob | File — Audio file content
filename string — Filename sent to the server
language string "en" ISO 639-1 language code

Returns: Promise<{ data: Analysis }>


client.analyses.list(params?)

List analyses for the authenticated user.

const { data: analyses } = await client.analyses.list({ limit: 10 });

for (const a of analyses) {
  console.log(a.id, a.filename, a.status);
}

Parameters

Parameter Type Description
limit number Maximum items per page (default server-side)
cursor string Pagination cursor from a previous response

Returns: Promise<{ data: Analysis[] }>


client.analyses.get(analysisId)

Retrieve a single analysis by ID.

const { data: analysis } = await client.analyses.get("uuid-here");

if (analysis.status === "completed") {
  console.log(analysis.transcript);
}

Returns: Promise<{ data: Analysis }>


client.analyses.delete(analysisId)

Delete an analysis permanently.

await client.analyses.delete("uuid-here");

Returns: Promise<void>


client.analyses.paginate(params?)

Async generator that yields every Analysis across all pages automatically.

for await (const analysis of client.analyses.paginate({ limit: 50 })) {
  console.log(analysis.id, analysis.filename);
}

Parameters: same as list().

Returns: AsyncGenerator<Analysis>


client.analyses.streamProgress(analysisId)

Returns a browser EventSource that streams server-sent pipeline progress events for an ongoing analysis.

const es = client.analyses.streamProgress("uuid-here");

es.addEventListener("pipeline_step", (e) => {
  const data = JSON.parse(e.data);
  console.log(data.step, data.progress_pct, "%");
});

es.addEventListener("done", () => {
  console.log("Pipeline complete");
  es.close();
});

SSE Event Types

Event name Payload fields Description
pipeline_step step, status, progress_pct, message, timestamp Progress update for one pipeline step
done — All steps finished

Returns: EventSource

Note: This method uses a ?token= query parameter to pass auth, because EventSource does not support custom headers.


Speakers

client.speakers.list()

List enrolled speaker profiles.

const { data: speakers } = await client.speakers.list();
speakers.forEach((s) => console.log(s.id, s.name));

Returns: Promise<{ data: Speaker[] }>


client.speakers.get(speakerId)

Retrieve a single speaker.

const { data: speaker } = await client.speakers.get("speaker-uuid");

Returns: Promise<{ data: Speaker }>


client.speakers.create(name)

Create a new speaker profile.

const { data: speaker } = await client.speakers.create("Alice");
console.log(speaker.id);

Returns: Promise<{ data: Speaker }>


client.speakers.delete(speakerId)

Delete a speaker profile.

await client.speakers.delete("speaker-uuid");

Returns: Promise<void>


Alerts

client.alerts.list(analysisId, options?)

List alerts generated for a specific analysis.

const { alerts, total } = await client.alerts.list("analysis-uuid", {
  acknowledged: false,  // filter to unacknowledged only
});

console.log(`${total} total alerts`);
alerts.forEach((a) => console.log(a.severityLevel, a.message));

Parameters

Parameter Type Description
analysisId string The analysis to fetch alerts for
options.acknowledged boolean | undefined Filter by acknowledged status

Returns: Promise<{ alerts: Alert[]; total: number }>


client.alerts.acknowledge(alertId)

Mark an alert as acknowledged.

const updated = await client.alerts.acknowledge("alert-uuid");
console.log(updated.acknowledged); // true

Returns: Promise<Alert>


client.alerts.stream()

Returns an EventSource for real-time SSE alert delivery. Alerts arrive as they are triggered during live streaming sessions.

const es = client.alerts.stream();

es.addEventListener("alert", (e) => {
  const alert = JSON.parse(e.data);
  console.log(alert.severityLevel, alert.message);
});

// Disconnect when done
es.close();

SSE Event Types

Event name Payload Description
alert Alert object A new alert has been triggered

Returns: EventSource


Stream

client.stream.live(config?)

Open a live WebSocket transcription session. The method connects to the WebSocket, sends the start action, and resolves only after the server responds with session_started.

const session = await client.stream.live({
  language: "en",
  sampleRate: 16000,
  scenarioId: "toxic_language_detection",
});

console.log("Session ID:", session.sessionId);

LiveSessionConfig

Property Type Default Description
language string "en" ISO 639-1 language code
sampleRate number 16000 Audio sample rate in Hz
scenarioId string — Optional scenario ID to enable real-time alert rules

Returns: Promise<LiveSession>


LiveSession

A handle to an open streaming session.

session.sessionId

The server-assigned session ID (string).

session.sendAudio(pcmBuffer)

Send raw PCM audio data. Audio must be 16-bit signed integers (Int16), mono, at the sampleRate specified when the session was created.

// In a Web Audio processor callback:
const int16 = new Int16Array(float32AudioData.length);
for (let i = 0; i < float32AudioData.length; i++) {
  int16[i] = Math.max(-32768, Math.min(32767, float32AudioData[i] * 32768));
}
session.sendAudio(int16.buffer);

session.on(event, handler)

Subscribe to a WebSocket event. Returns an unsubscribe function.

const unsub = session.on("transcript_final", (event) => {
  console.log(event.data?.text);
});

// Later:
unsub();

Use "*" to receive all events.

Event names

Event Description
session_started Session is active; data.sessionId is set
transcript_partial In-progress transcript text
transcript_final Committed transcript text with word timestamps
alert Alert triggered by the scenario rules engine
analysis_started Post-stream analysis pipeline started; analysis_id in payload
session_ended Session has ended; may include analysis_id
ping Server keepalive
error Server-side error; data.message contains details

session.stop()

Send a { action: "stop" } signal to the server to end the session gracefully.

session.stop();

session.close()

Immediately close the WebSocket connection.

session.close();

Full Streaming Example

import { Perceive8Client } from "perceive8";

const client = new Perceive8Client({ apiKey: process.env.PERCEIVE8_API_KEY! });

async function runLiveSession() {
  const session = await client.stream.live({
    language: "en",
    sampleRate: 16000,
    scenarioId: "sales_coaching",
  });

  const unsubPartial = session.on("transcript_partial", (e) => {
    process.stdout.write(`\r[partial] ${e.data?.text}`);
  });

  const unsubFinal = session.on("transcript_final", (e) => {
    console.log(`\n[final]   ${e.data?.text}`);
  });

  session.on("alert", (e) => {
    console.warn("[ALERT]", e.data);
  });

  session.on("session_ended", () => {
    console.log("\nSession ended.");
    unsubPartial();
    unsubFinal();
  });

  // ---- Send audio here ----
  // session.sendAudio(pcmInt16Buffer);

  // Stop after 60 s
  await new Promise((r) => setTimeout(r, 60_000));
  session.stop();
  session.close();
}

runLiveSession().catch(console.error);

Pagination

The paginate utility from the analyses resource handles cursor-based pagination automatically:

// Using the built-in async generator
for await (const analysis of client.analyses.paginate({ limit: 100 })) {
  console.log(analysis.id);
}

For custom pagination, use the low-level paginate helper exported from the package:

import { paginate } from "perceive8";

const allAnalyses = [];
for await (const item of paginate((cursor) =>
  client.analyses.list({ limit: 50, cursor })
)) {
  allAnalyses.push(item);
}

The Page<T> type returned by individual list() calls:

interface Page<T> {
  items: T[];
  nextCursor?: string;
  hasMore: boolean;
}

Error Handling

All SDK methods throw typed error subclasses:

Class HTTP status Description
Perceive8Error any >= 400 Base error class — most general
AuthenticationError 401 Invalid or missing credentials
NotFoundError 404 Resource does not exist
RateLimitError 429 Rate limit exceeded

The SDK automatically retries 429 responses up to 3 times with exponential back-off (1s, 2s, 4s).

import { Perceive8Error, AuthenticationError, NotFoundError, RateLimitError } from "perceive8";

try {
  const { data } = await client.analyses.get("bad-id");
} catch (err) {
  if (err instanceof NotFoundError) {
    console.error("Analysis not found");
  } else if (err instanceof AuthenticationError) {
    console.error("Check your API key");
  } else if (err instanceof RateLimitError) {
    console.error("Slow down — rate limit hit");
  } else if (err instanceof Perceive8Error) {
    console.error(`API error ${err.statusCode}:`, err.response);
  } else {
    throw err;
  }
}

Error Properties

class Perceive8Error extends Error {
  statusCode?: number;  // HTTP status code
  response?: unknown;   // Parsed JSON body from the API
}

TypeScript Models & Types

All types are exported from the perceive8 package root.

Analysis

type AnalysisStatus = "pending" | "processing" | "completed" | "failed";

interface Analysis {
  id: string;
  user_id: string;
  filename: string;
  status: AnalysisStatus;
  created_at: string;          // ISO 8601
  completed_at?: string;       // ISO 8601, set when status = "completed"
  transcript?: unknown;        // Full transcript object when completed
}

Speaker

interface Speaker {
  id: string;
  user_id: string;
  name: string;
  created_at: string;
}

Alert

interface Alert {
  id: string;
  analysisId: string;
  userId: string;
  scenarioId?: string;
  severityLevel: "INFO" | "WARNING" | "HIGH" | "CRITICAL";
  message: string;
  evidenceText?: string;
  speakerName?: string;
  startTime?: number;
  endTime?: number;
  confidence?: number;
  acknowledged: boolean;
  createdAt: string;
}

PipelineEvent

interface PipelineEvent {
  analysisId: string;
  step: string;
  status: "started" | "completed" | "failed";
  progressPct: number;
  message: string;
  timestamp: string;
}

TranscriptSegment

interface TranscriptSegment {
  text: string;
  isFinal: boolean;
  audioStart: number;
  audioEnd: number;
  confidence: number;
  words: Array<{
    text: string;
    start: number;
    end: number;
    confidence: number;
  }>;
}

StreamSession

interface StreamSession {
  sessionId: string;
  expiresAt: string;
}

TranscriptEvent (WebSocket message)

interface TranscriptEvent {
  event:
    | "transcript_partial"
    | "transcript_final"
    | "alert"
    | "session_started"
    | "session_ended"
    | "ping";
  data?: {
    text?: string;
    isFinal?: boolean;
    audioStart?: number;
    audioEnd?: number;
    confidence?: number;
    words?: Array<{ text: string; start: number; end: number; confidence: number }>;
    sessionId?: string;
    [key: string]: unknown;
  };
  sessionId?: string;
}

LiveSessionConfig

interface LiveSessionConfig {
  language?: string;    // default "en"
  sampleRate?: number;  // default 16000
  scenarioId?: string;
}

ApiKey

interface ApiKey {
  id: string;
  name: string;
  key_prefix: string;
  scopes: string[];
  rate_limit_rpm: number;
  is_active: boolean;
  created_at: string;
}

Page<T>

interface Page<T> {
  items: T[];
  nextCursor?: string;
  hasMore: boolean;
}