Skip to content
Skip to page content

Live calling

Connect the SDK to Trellus telephony with the browser factory or an explicit transport. Live calling needs a Trellus account, a rep API key, the Twilio Voice SDK and enablement by Trellus.

View as Markdown

By arrangement with Trellus

What live calling needs

Anyone can install the packages and build against the mock transport. Placing a real call needs all of the following:

  • A Trellus account.
  • A rep API key. Your application supplies it through getApiKey.
  • The Twilio Voice SDK, @twilio/voice-sdk version >=2.18.0 <3, installed alongside the SDK.
  • Enablement by Trellus. A line setting in code does not grant entitlement.
This is an integration preview. It is a standalone subset of the Trellus Chrome extension and does not claim full parity with it. Your application stays responsible for which contacts may be called and when. See status and scope.

Talk to Trellus about live access. Already have a Trellus account? Contact Trellus support.

createBrowserDialer

The browser entry point is the smallest live integration. createBrowserDialer imports the Twilio Voice SDK lazily and wires the headless SDK to the live transport.

Create a live dialer in the browser
import { createBrowserDialer } from '@trellusrelease/dialer-sdk/browser';

const dialer = await createBrowserDialer({ getApiKey: getAuthenticatedRepTrellusKey, maxLines: 3 });
// A user action starts registration; creating the object has no calling side effects.
await dialer.connect();

getAuthenticatedRepTrellusKey is yours to implement. The package does not supply it.

  • Creating the dialer has no calling side effects. It does not request a credential or register a device until connect().
  • connect() requests microphone access and prepares input audio. Call it from a real user action so the browser can activate audio.
  • Each instance registers under its own generated suffix. That keeps audio routing separate. It does not lock queue ownership across tabs.
createBrowserDialer options
OptionType and meaning
getApiKey Required () => string | Promise<string> Your function that returns the signed-in rep’s Trellus API key. Raw credential options are rejected.
maxLines Optional number Client line ceiling, 1 to 4. Default 1. Configuration does not grant entitlement.
platform Optional string The platform value agreed with Trellus. Defaults to TRELLUS_DIALER.
onDisposition Optional (result: { call: DialCall; disposition: Disposition }) => void | Promise<void> Saves a completed wrap-up through your application.
Device Optional Twilio Device constructor Pass it to reuse a Twilio import your application already owns. When omitted, the factory imports @twilio/voice-sdk lazily.
transportOptions Optional object Service and runtime configuration only: routerUrl, bootstrapUrl, fetch, WebSocket, mediaDevices, deviceOptions, deviceSuffix and timeoutMs. Credentials, Device, platform and maxLines belong at the top level.

createTrellusTransport

Build the transport yourself when you want to pass your own Twilio Device import or keep the transport and the dialer separate. Set maxLines on both.

Explicit transport construction
import { Device } from '@twilio/voice-sdk';
import { createDialer } from '@trellusrelease/dialer-sdk';
import { createTrellusTransport } from '@trellusrelease/dialer-sdk/transport';

const transport = createTrellusTransport({
  Device,
  getApiKey: async () => getAuthenticatedRepTrellusKey(),
  platform: 'TRELLUS_DIALER',
  maxLines: 3,
  deviceSuffix: `sdk-${crypto.randomUUID()}`,
});

const dialer = createDialer({
  transport,
  maxLines: 3,
  onDisposition: async ({ call, disposition }) => {
    // Your authenticated application persists the exact task/attempt outcome.
    // Resolve only when your own save contract succeeds.
    await saveCallOutcome({
      taskId: call.taskId,
      attemptId: call.attemptId,
      sessionId: call.sessionId,
      ...disposition,
    });
  },
});

// Invoke from a genuine user action so browser audio can activate.
await dialer.connect();
await dialer.dial({ id: 'task-1', name: 'Alex Example', phoneNumber: '+12025550123' });

getAuthenticatedRepTrellusKey and saveCallOutcome are yours to implement. They are not endpoints supplied by the package.

createTrellusTransport options
OptionType and meaning
getApiKey Required () => string | Promise<string> Returns the signed-in rep’s credential. The transport never persists it.
Device Required Twilio Device constructor The Device constructor from @twilio/voice-sdk. The package does not bundle Twilio.
platform Required string The platform value agreed with Trellus, usually TRELLUS_DIALER.
maxLines Required number Line cap, 1 to 4. Configuration does not grant entitlement.
timeoutMs Optional number Time limit in milliseconds for credential retrieval, HTTP exchanges and device setup. Default 15000. Accepts 1 to 120000.
deviceOptions Optional Record<string, unknown> Options for the Twilio Device.
deviceSuffix Optional string A distinct client registration: 1 to 64 letters, digits, underscores or hyphens. The transport adds no suffix unless you supply one.
mediaDevices Optional Pick<MediaDevices, 'getUserMedia'> Used for the microphone permission and capture check during connect().
fetch, WebSocket Optional typeof fetch, typeof WebSocket Injectable for controlled testing.
routerUrl, bootstrapUrl Optional string Service URL overrides for controlled testing. They must use HTTPS. Treat them as trusted deployment configuration, never as end-user or contact input.

The transport is a browser adapter. It needs no extension, DOM interface or Chrome APIs. It reports its line cap to the dialer when it connects.

In this preview the transport delivers call status, transcripts and detection values. It does not deliver call summaries. Integrate durable final call data separately, through a reporting or webhook path agreed with Trellus.

The Twilio Voice SDK

Live audio runs through the Twilio Voice SDK in the rep’s browser. Install @twilio/voice-sdk, version >=2.18.0 <3, alongside the SDK.

Install the SDK and the Twilio Voice SDK
npm install @trellusrelease/dialer-sdk @twilio/voice-sdk
  • The packages do not bundle Twilio. It is an optional peer dependency.
  • createBrowserDialer imports it lazily. Pass Device to reuse an import your application already owns.
  • createTrellusTransport takes the Device constructor as a required option.
  • If the dialer runs inside an iframe, the iframe must permit microphone use.
  • The mock transport does not contact Twilio.

Credentials

The SDK authenticates as one rep. Your application supplies that rep’s Trellus API key through getApiKey.

  • Supply the key through getApiKey only. It is your function. createBrowserDialer rejects raw credential options, and transportOptions accepts none.
  • Keep the key in memory. The transport never persists it.
  • Keep the key out of URLs, task metadata, logs and telemetry. Public snapshots, session views and event payloads never contain API keys, device tokens or transport session handles.
  • One active dialer per rep. A distinct device registration avoids identity collisions. It does not authorize concurrent dialers for one rep, and the SDK has no cross-tab ownership lease. Your application coordinates one active owner per rep.
  • Do not surface provider errors. Audio and device controls can reject with errors from the audio provider. Do not show those messages to users or write them to telemetry. Use the sanitized SDK code and message.

Review credential handling with Trellus before you put a rep key in a browser application.

Line limits

  • The headless core accepts maxLines from 1 to 10. The default is 1.
  • The supplied Trellus transport and createBrowserDialer accept 1 to 4.
  • The effective limit is the smaller of the dialer’s setting and the cap the transport reports. Read it from snapshot.maxLines.
  • A line setting in code does not grant entitlement.
  • The preview does not fetch a team’s or a rep’s dialing settings, and it does not authorize a requested line count. Confirm the line count with Trellus before you set it.

Custom transports

The headless SDK talks to any object that implements DialerTransport. Use the supplied Trellus transport for live calling and the mock transport for local work. Implement the interface yourself only for a server-mediated integration agreed with Trellus.

DialerTransport
interface TransportSession { readonly id: string }

interface DialerTransport<Session extends TransportSession = TransportSession> {
  connect(): Promise<void | { maxLines?: number }>;
  prepare?(options: { mode: DialMode; lines: number }): Promise<void>;
  createCall(options: {
    attemptId: string;
    task: DialTask;
    mode: DialMode;
    lines: number;
    canStartCall: () => boolean;
  }): Promise<Session>;
  subscribe(session: Session, listener: (event: TransportEvent) => void): (() => void) | void;
  hangup(session: Session): Promise<void>;
  mute?(session: Session, muted: boolean): Promise<void>;
  sendDigits?(session: Session, digits: string): Promise<void>;
  getAudioDevices?(): Promise<AudioDevices>;
  setInputDevice?(id: string): Promise<void>;
  setOutputDevice?(id: string): Promise<void>;
  dispose(): void | Promise<void>;
}

A transport reports what happens to a call with this small set of events:

Transport events
{ type: 'ringing' }
{ type: 'connected' } // Rep actually bridged; not merely destination answered.
{ type: 'ended' }
{ type: 'transcript', segments: TransportTranscriptSegment[] }
{ type: 'detection', value: string }
{ type: 'summary', text: string }
{ type: 'error', error?: { code?: string, outcomeUnknown?: boolean } }

Rules a transport must follow

  • Each session handle needs a stable, nonempty id of at most 256 characters. The SDK keeps the handle private. Only the id is exposed, as sessionId.
  • Check canStartCall() immediately before dispatch, after any asynchronous preparation. A cancellation before dispatch must not place a call.
  • If a create may have been dispatched but its result is uncertain, reject with outcomeUnknown: true.
  • Emit connected only with evidence that this rep joined this exact session. A destination that answers is not enough.
  • Correlate every event to its exact handle, and keep events that arrive before subscribe is called.
  • Never report a lost event stream as ended.
  • A transcript event supplies the current segment set, not an append instruction. Final transcript updates may arrive after ended.
  • Set isFinal: true on a segment only when finality is confirmed. When it is omitted, the public output shows false.
  • Reject unsupported features explicitly instead of pretending success.

When a transport returns an invalid or reused session ID, the SDK treats the result as uncertain. It keeps the capacity as unknown, emits no session.created and does not subscribe to that handle. See uncertain outcomes.