Web / Contact Centre SDKLatest (2.x)
Web · JavaScript SDK

Integrate the SecuredCalls Web SDK

The SecuredCalls Web SDK is a JavaScript/TypeScript SDK for web apps and web-based dialers. It handles two things: branding calls and running on-device biometric verification — it does not place the call itself.

Estimated time: 35 minutes

Node.jsBrowser / WebTypeScriptnpm

Overview

The Web SDK covers the branding and verification lifecycle that happens before, during, or after a call. It works server-side in Node.js or client-side in the browser, and is token-only — your business secret never enters the browser.

The SDK provides two capabilities:

  • Call branding — set up, clear, and annotate branded calls.
  • Biometric verification — over a real-time data channel, ask the consumer’s device to perform an on-device biometric check (Face ID / Touch ID) and receive the result.
Important:

This SDK does not manage the actual voice or video call. It is solely responsible for the branding and verification lifecycle that happens before, during, or after a call session.

Note:

You build your own UI. The SDK exposes data and events; rendering status panels, verify buttons, and result badges is up to your application. The examples below show the recommended patterns — adapt them to your framework.

Topics

Jump to any part of the integration.

Prerequisites

Make sure your environment is ready.

  • A SecuredCalls account and your configuration string from the Secured Calls Portal
  • A way to mint short-lived access tokens server-side using your business secret
  • Node.js and npm (for the Node.js integration), or any web page (for the browser integration)
  • For the browser build, the bundled JavaScript file from the SecuredCalls team
1

Section 1

Install the SDK

Choose the install path that matches your environment. The browser build is distributed separately by the SecuredCalls team.

STEP 1

Install for Node.js

Install the SDK from npm into your Node.js project.

terminalbash
1npm install @expertstack-studios/sc-web-sdk-v2
STEP 2

Include the browser build

For a web application, include the compiled SDK file directly in your HTML.

Note:

Currently you will have to get in touch with the Secured Calls team to download the bundled JavaScript file.

index.htmlhtml
1<script src="secured-calls-web-sdk-vx.x.x.js"></script>
2

Section 2

Initialize the client

The SDK is token-only. You obtain a short-lived access token server-side and pass it to the SDK. The SDK sends that bearer token alone — no secret, no client-side signing or encryption — so your business secret never enters the browser.

STEP 3

Create the client

Construct SecuredCalls with your configuration, a server-issued token, and an optional loggingConfig.

Note:

configuration is available from the Secured Calls Portal. The token must be minted on your server using your secret — see Acquire an access token below — and handed to the browser. Never embed your secret in client-side code.

index.tstypescript
1import { SecuredCalls } from '@expertstack-studios/sc-web-sdk-v2';2 3const scClient = new SecuredCalls({4    configuration: '<your-configuration>',5    token: '<access-token-issued-server-side>',6    loggingConfig: { level: 'info' },7});

Options

Constructor options passed to new SecuredCalls({ … }).

configurationstring

Configuration string for your contact center / app, from the Secured Calls Portal. Required.

tokenstring

A bearer access token, issued server-side. Required.

loggingConfig{ level: 'info' | 'debug' | 'error' }

Optional. Log verbosity — useful during integration.

3

Section 3

Acquire an access token

The SDK does not fetch its own token — your server mints one and passes it to the browser. This keeps your secret server-side. The configuration string is not sensitive — it only carries your clientId and the API/WebSocket hostnames; only the secret must stay server-side.

STEP 4

Call the auth-token endpoint

Your server authenticates to the Secured Calls auth-token endpoint with HTTP Basic auth.

  • secret — your business secret from the Secured Calls Portal.
  • {clientId} and {domain} are encoded in your configuration string. Base64-decode configuration to get a colon-separated clientId:domain:wsDomain; use the first field as clientId and the second as domain (defaults to au.api.securedcalls.com if absent).
HTTPswift
1POST https://{domain}/branding/cc/auth-token2Authorization: Basic base64("{clientId}:{secret}")
STEP 5

Read the response

The response contains the bearer token and its expiry.

response.jsonjson
1{2    "scResponse": {3        "code": "...",4        "message": "...",5        "data": {6            "token": "<bearer-token>",7            "expiresAt": "<ISO-8601 timestamp>"8        }9    }10}
STEP 6

Expose a token endpoint (Node.js / Express)

Expose an authenticated endpoint your frontend can call. Keep the secret in server config / environment variables, never in client code.

Important:

Protect this route with your own auth — only logged-in agents should reach it. Return only the token (and expiry) to the browser, never the secret.

server.tstypescript
1// server-side only2const CONFIGURATION = process.env.SC_CONFIGURATION!; // base64 "clientId:domain:wsDomain"3const SECRET = process.env.SC_SECRET!;               // never sent to the browser4 5const [clientId, domain = 'au.api.securedcalls.com'] =6    Buffer.from(CONFIGURATION, 'base64').toString('utf8').split(':');7 8app.post('/sc/token', async (req, res) => {9    // Protect this route with your own auth — only logged-in agents should reach it.10    const credentials = Buffer.from(`${clientId}:${SECRET}`).toString('base64');11 12    const scRes = await fetch(`https://${domain}/branding/cc/auth-token`, {13        method: 'POST',14        headers: {15            'Content-Type': 'application/json',16            Authorization: `Basic ${credentials}`,17        },18    });19 20    if (!scRes.ok) {21        return res.status(502).json({ error: `auth-token failed: ${scRes.status}` });22    }23 24    const body = await scRes.json();25    const token = body?.scResponse?.data?.token;26    const expiresAt = body?.scResponse?.data?.expiresAt;27    if (!token) {28        return res.status(502).json({ error: 'no token in response' });29    }30 31    // Return only the token (and expiry) to the browser — never the secret.32    res.json({ token, expiresAt });33});
STEP 7

Fetch the token, then build the SDK

In the browser, fetch a token from your own endpoint, then construct SecuredCalls with it.

Note:

Token lifetime: tokens are short-lived (expiresAt). Acquire a fresh token per session/call rather than caching one long-term, and construct a new SecuredCalls instance with the new token when it changes.

client.tstypescript
1import { SecuredCalls } from '@expertstack-studios/sc-web-sdk-v2';2 3// Your own endpoint from the server example above.4const { token } = await fetch('/sc/token', { method: 'POST' }).then((r) => r.json());5 6const scClient = new SecuredCalls({7    configuration: '<your-configuration>', // safe in the browser (no secret inside)8    token,9});
4

Section 4

Call Branding

Subscribe to the branding events, then set up branding for a call. On success you receive a branding reference ID — keep it to clear branding or add notes later.

STEP 8

Brand a call

Register the three branding callbacks, then call setupBrandingAsync(...):

  • onBrandingSuccess — fires with a referenceId; start your call here and keep the referenceId to clear branding later.
  • onBrandingFailed — handle branding failures.
  • onBrandingTimedOut — handle branding timeouts.

Use clearBrandingAsync(referenceId) when the call ends, and addNoteAsync(referenceId, note) to attach a read-only note shown to the recipient.

Tip:

Pass "PSTNCall" or "InAppCall" as the call type (default "InAppCall"), and a timeout in milliseconds before the attempt is considered failed (default 20000).

index.tstypescript
1import { SecuredCalls } from '@expertstack-studios/sc-web-sdk-v2';2 3const scClient = new SecuredCalls({4    configuration: '<your-configuration>',5    token: '<access-token>',6    loggingConfig: { level: 'info' },7});8 9scClient.onBrandingSuccess((referenceId) => {10    console.log(`Branding succeeded: ${referenceId}`);11});12scClient.onBrandingFailed((error) => {13    console.error(`Branding failed: ${error}`);14});15scClient.onBrandingTimedOut(() => {16    console.warn('Branding timed out.');17});18 19await scClient.setupBrandingAsync(fromNumber, toNumber, 'support', 'PSTNCall', 20000);
5

Section 5

Biometric Verification

Biometric verification runs over a real-time data channel. Open the channel when the call connects, register result listeners, trigger a check from your UI, and close the channel when the call ends.

STEP 9

Open the data channel

When the call connects, call callConnectedAsync(...). The response tells you whether the channel is enabled and whether biometrics are available on this number.

All three fields are required: businessNumber, consumerIdentifier, and uniqueContactCenterCallId.

Note:

channel.enabled is false when verification is unavailable for the number. The channel can be open while channel.biometric.enabled is false — check both before offering a Verify button.

verification.tstypescript
1import { SecuredCalls } from '@expertstack-studios/sc-web-sdk-v2';2 3const scClient = new SecuredCalls({4    configuration: '<your-configuration>',5    token: '<access-token>',6});7 8// Open the data channel once the call is connected.9const channel = await scClient.callConnectedAsync({10    businessNumber: '61111222333',          // your business number on the call11    consumerIdentifier: '61123456789',      // the consumer's number12    uniqueContactCenterCallId: '<call-id>', // your unique id for this call13});14 15if (!channel.enabled) {16    // Verification is not available on this number.17} else if (!channel.biometric.enabled) {18    // The channel is open but biometrics are not enabled for this number.19} else {20    // Biometrics are available — register listeners and trigger a check.21}
STEP 10

Listen for outcomes

Register onBiometricResult(...) and onBiometricError(...) before triggering a check — the outcome arrives asynchronously on these listeners, not as the return value of the trigger call.

verification.tstypescript
1// Listen for outcomes before triggering a check.2scClient.onBiometricResult((result) => {3    if (result.verified) {4        console.log('Verified', result.reason); // e.g. "FaceIdMatch"5    } else {6        console.log('Not verified', result.reason);7    }8});9 10scClient.onBiometricError((err) => {11    console.warn(`Biometric error: ${err.code}  ${err.message}`);12});
STEP 11

Trigger a check

Call triggerBiometricAsync() — typically from a Verify button. The consumer’s device prompts for Face ID / Touch ID, and the outcome arrives on your listeners.

Important:

The data channel must be open before you trigger a check, or the request fails with CallNotConnected.

verification.tstypescript
1// Trigger an on-device check (e.g. from a "Verify" button).2await scClient.triggerBiometricAsync();
STEP 12

Close the channel

Call callDisconnectedAsync() when the call ends. It is safe to call even if the channel was never connected.

verification.tstypescript
1// Close the channel when the call ends.2await scClient.callDisconnectedAsync();
6

Section 6

Build a verification UI

The SDK gives you raw outcomes; you decide how to present them. The reason on a result is a device-supplied string such as FaceIdMatch or TouchIdMatch.

STEP 13

Map reasons to friendly labels

Map raw reasons to labels your agents can read. Keep a default branch that falls back to the raw value so a new device reason still renders rather than disappearing.

Tip:

Reasons and error codes are an open set — always keep a default/fallback branch so unmapped values are shown rather than dropped.

describeBiometricReason.tstypescript
1/** Map a raw biometric reason to a user-friendly label. Fall back to the raw2 *  value so a new device reason still renders rather than disappearing. */3function describeBiometricReason(reason: string): string {4    switch (reason) {5        case 'FaceIdMatch':            return 'Face ID';6        case 'TouchIdMatch':           return 'Touch ID';7        case 'FaceIdMismatch':         return 'Face ID  no match';8        case 'TouchIdMismatch':        return 'Touch ID  no match';9        case 'BiometricMatch':10        case 'BiometricVerified':      return 'Biometric match';11        case 'BiometricMismatch':      return 'Biometric  no match';12        case 'CancelledByUser':        return 'Cancelled by consumer';13        case 'CancelledByAgent':       return 'Cancelled by agent';14        case 'BiometricNotEnrolled':   return 'No biometrics enrolled';15        case 'BiometricPermissionDenied': return 'Permission denied';16        case 'BiometricUnavailable':   return 'Biometrics unavailable';17        case 'BiometricLockedOut':     return 'Biometrics locked';18        default:                       return reason; // unknown — show as-is19    }20}
STEP 14

Wire it to a minimal flow

Count attempts, render each outcome with its friendly label, surface errors, and trigger a check from your Verify button.

verifyWidget.tstypescript
1let attempts = 0;2 3scClient.onBiometricResult((result) => {4    attempts += 1;5    const label = result.reason ? describeBiometricReason(result.reason) : '';6    renderAttempt({7        n: attempts,8        verified: result.verified,9        detail: label, // e.g. "Verified · Face ID"10    });11});12 13scClient.onBiometricError((err) => {14    showError(err.message); // e.g. "Maximum biometric attempts reached for this call."15});16 17// Called from your "Verify" button:18async function onVerifyClicked() {19    await scClient.triggerBiometricAsync();20}
7

Section 7

Browser quick start

The browser build exposes a global SecuredCallsSDK. Include the bundled script, create the client with your configuration and token, wire the success callback, and start branding.

STEP 15

Brand a call from the browser

Create the client with new SecuredCallsSDK.SecuredCalls({ configuration, token }), then call setupBrandingAsync(...).

Important:

Use a short-lived, server-issued token here — never expose a production secret in a public web page.

index.htmlhtml
1<script src="secured-calls-web-sdk-vx.x.x.js"></script>2<script>3    const scClient = new SecuredCallsSDK.SecuredCalls({4        configuration: '<your-configuration>',5        token: '<access-token>',6    });7 8    scClient.onBrandingSuccess((referenceId) => {9        console.log(`Branding successful: ${referenceId}`);10    });11 12    scClient.setupBrandingAsync('+61123456789', '+61111222333', 'support', 'PSTNCall', 20000);13</script>
8

Section 8

Branding API

The SecuredCalls client exposes three async methods for managing call branding and notes.

STEP 16

setupBrandingAsync

Initiates branding. Resolves to a branding reference ID.

Declaration
setupBrandingAsync(fromNumber: string, toNumber: string, intent: string, callType?: string, timeout?: number): Promise<string>
fromNumberstring

The number initiating the call.

toNumberstring

The number receiving the call.

intentstring

The intent of the call (e.g. support).

callTypestring

Optional. 'PSTNCall' or 'InAppCall' (default 'InAppCall').

timeoutnumber

Optional. Milliseconds before the attempt is considered failed (default 20000).

STEP 17

clearBrandingAsync

Clears branding that was set up earlier.

Declaration
clearBrandingAsync(referenceId: string): Promise<boolean>
referenceIdstring

The reference ID returned by setupBrandingAsync.

STEP 18

addNoteAsync

Attaches a read-only note shown to the recipient.

Declaration
addNoteAsync(referenceId: string, note: string): Promise<boolean>
referenceIdstring

The reference ID of the call to attach the note to.

notestring

The note to attach to the call.

Branding events

Callbacks the client emits during the branding lifecycle.

  • onBrandingSuccess(referenceId) — branding succeeded; referenceId identifies the branded call.
  • onBrandingFailed(errorMessage) — branding failed.
  • onBrandingTimedOut() — branding timed out.
9

Section 9

Biometric API

Four methods manage the data channel and on-device verification.

STEP 19

callConnectedAsync

Opens the data channel. Returns channel and biometric availability.

Declaration
callConnectedAsync(params: { businessNumber: string, consumerIdentifier: string, uniqueContactCenterCallId: string }): Promise<IDataChannelConnectResponse>
businessNumberstring

Your business number on the call. Required.

consumerIdentifierstring

The consumer’s number. Required.

uniqueContactCenterCallIdstring

Your own unique identifier for this call. Required.

The resolved IDataChannelConnectResponse:

IDataChannelConnectResponse.tstypescript
1interface IDataChannelConnectResponse {2    enabled: boolean;                  // false → channel was not opened3    dataChannelConnectionId?: string;  // present when enabled and registered4    biometric: {5        enabled: boolean;6        maxAttemptsPerCall?: number;   // verification attempts allowed per call7    };8}
STEP 20

triggerBiometricAsync

Requests an on-device biometric check. Requires the channel to be open. The result is delivered asynchronously to onBiometricResult / onBiometricError.

Declaration
triggerBiometricAsync(): Promise<void>
STEP 21

callDisconnectedAsync

Closes the data channel. Safe to call even if not connected.

Declaration
callDisconnectedAsync(): Promise<void>
STEP 22

getDataChannelConfig

Returns the config from the most recent callConnectedAsync — available even if the socket failed — or null.

Declaration
getDataChannelConfig(): IDataChannelConfig | null

Biometric events

Callbacks the client emits during verification.

onBiometricResult(result) — a check completed.

IBiometricResult.tstypescript
1interface IBiometricResult {2    requestId: string;3    verified: boolean;4    reason?: string;   // device-supplied, e.g. "FaceIdMatch", "TouchIdMatch"5}

onBiometricError(error) — the request was rejected or could not run.

IBiometricError.tstypescript
1interface IBiometricError {2    code: string;      // see codes below3    message: string;4}

Biometric error codes

Treat error.code as a string enum — switch on the ones you handle and default for the rest, as new codes may be added.

CodeMeaning
CallNotConnectedThe call isn’t connected yet — retry shortly.
BiometricDisabledBiometrics are not enabled for this application.
BiometricMaxAttemptsReachedMaximum attempts for this call have been used.
BiometricAlreadyInProgressA verification is already running.
RecipientNotConnectedThe consumer’s device is not connected to the channel.
InvalidRequestThe request was rejected as invalid.

Implementation time

A typical end-to-end integration takes about 35 minutes.

TaskEstimated time
Install the SDK2 min
Initialize the client2 min
Mint access tokens server-side10 min
Wire up branding callbacks3 min
Set up branding for a call3 min
Clear branding & add notes5 min
Open & close the data channel4 min
Trigger biometric verification3 min
Build the verification UI3 min

Total: ~35 minutes