> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracklysms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect Your Salesforce Dialer

> Keep your existing Salesforce dialer and connect its backend, headset audio, and call controls to Trackly.

Keep your Salesforce interface, contact selection, user login, and backend.
Replace the calling integration in three places:

| Existing integration                     | Trackly replacement                                                          |
| ---------------------------------------- | ---------------------------------------------------------------------------- |
| Backend issues a Twilio browser token    | Backend creates a Trackly browser session for the authenticated salesperson. |
| Twilio browser SDK manages headset audio | Trackly browser SDK connects the headset to Trackly's media service.         |
| Call button and telephony controls       | Bind the same buttons to Trackly's SDK methods and backend callbacks.        |

The salesperson clicks your existing Call button and speaks through their
headset. Trackly handles the browser media connection and telephone call. This
requires adapting your calling code; the SDK is not a Twilio-compatible drop-in.

<Note>
  Browser calling requires activation for your Trackly account. Complete your
  account's calling setup and confirm activation with Trackly before testing.
  A downloaded SDK or an API key alone does not enable live calls.
</Note>

## Before you start

Trackly supplies or confirms:

* Your account and a server-held API key with `voice_calls.read` and
  `voice_calls.write`.
* Your voice-enabled caller number, approved media origins, call/session limits,
  and an API quota sized for your active agents.
* The account's calling activation and commercial terms.

Download the [browser SDK preview](https://app.tracklysms.com/downloads/trackly-voice-browser-preview.js)
and retain it in your existing application's approved script/static-resource
setup. It includes SIP.js and license notices; no additional SIP.js installation
is needed. [Version and checksum metadata](https://app.tracklysms.com/downloads/trackly-voice-browser-preview.json)
accompanies the bundle.

If you serve several client businesses, your backend selects each client's
server-held Trackly key from the authenticated user's authorized client mapping.
Keep caller numbers, agents, sessions, and calls scoped to that client.

## 1. Add Trackly to your existing backend

The Trackly API base URL is:

```text theme={null}
https://api.tracklysms.com/api/v1/voice
```

Use `Authorization: Bearer <Trackly API key>` and `Content-Type: application/json`
for server-side requests. Keep that key in your existing backend's secret store.
The browser receives only temporary headset credentials.

Create one reusable browser agent per salesperson with
[Create an Agent](/api-reference/voice/agents/create-agent):

```http theme={null}
POST /api/v1/voice/agents

{
  "name": "Salesforce agent <unique user label>",
  "type": "webrtc",
  "externalSubject": "sf:<authenticated-org-id>:<authenticated-user-id>"
}
```

Derive `externalSubject` from your authenticated server-side identity. Store and
reuse the returned agent mapping under the same issuing API key; omit `number`.
After an uncertain creation response or `409 agent_exists`, use
[List Agents](/api-reference/voice/agents/list-agents) to find the intended mapping.

Expose these operations through your existing authenticated backend. The SDK
calls your functions; those functions call Trackly:

| SDK callback        | Your backend calls Trackly                                          |
| ------------------- | ------------------------------------------------------------------- |
| `createSession`     | `POST /browser-sessions` with the server-derived `externalSubject`. |
| `getCurrentSession` | `GET /browser-sessions?externalSubject=...` for the same user.      |
| `createCall`        | `POST /browser-calls` with `sessionId`, `from`, and `to`.           |
| `getCall`           | `GET /browser-calls/{id}`.                                          |
| `hangupCall`        | `POST /browser-calls/{id}/hangup` with JSON `{}`.                   |
| `endSession`        | `DELETE /browser-sessions/{id}` with JSON `{}`.                     |

Session and call creation require an `Idempotency-Key`. Persist the attempt key
and original input before submission, enforce user ownership on every operation,
and return the parsed response envelope unchanged. A timeout must not trigger a
new call attempt. The [callback reference](/api-reference/voice/salesforce-configuration#backend-callbacks)
contains exact signatures, responses, and an adapter example for these six methods.

## 2. Replace the browser audio integration

Load the downloaded script once in the same component that owns your current
softphone. It exposes `window.TracklyVoice.createVoiceBrowser`.

This example uses your existing backend adapter, audio element, selected devices,
and status renderer. They are application dependencies, not new Trackly services:

```js theme={null}
const voice = window.TracklyVoice.createVoiceBrowser({
  backend: tracklyBackend,
  remoteAudio: existingAudioElement,
  audioDevices: {
    microphoneId: selectedMicrophoneId,
    speakerId: selectedSpeakerId,
  },
  onStatus: renderCallStatus,
});

async function enableHeadset() {
  await voice.recover();
  if (voice.status().connection === "recovery_required") return;
  await voice.connect();
}
```

Run `enableHeadset()` once on a fresh helper when the user enables your softphone.
It checks for earlier work and then connects the headset. If recovery is required,
show an explicit cleanup action using `voice.disconnect()` and wait for confirmed
cleanup before connecting. Keep this helper for later calls in the same component.

Use HTTPS, allow microphone access, and retain your existing playback control for
browser autoplay restrictions. Device selections are fixed for one helper
instance. If your existing Salesforce wrapper needs new script or media origins,
see the [optional Salesforce configuration reference](/api-reference/voice/salesforce-configuration#salesforce-packaging).
You can keep your current backend and Salesforce component type.

## 3. Connect your existing buttons

Your Call button supplies the selected owned caller number and contact number,
both in E.164 format:

```js theme={null}
async function callContact(from, to) {
  const state = voice.status();
  if (state.connection !== "ready" || state.busy) return;
  await voice.startCall(
    { from, to },
    { idempotencyKey: crypto.randomUUID() },
  );
}
```

Use your existing click lock while a handler is pending, and show rejected
promises using their safe `error.code`. Do not automatically retry call creation.

| Existing control              | SDK action                                                    |
| ----------------------------- | ------------------------------------------------------------- |
| Hang up                       | `await voice.hangup()`                                        |
| Mute / unmute                 | `await voice.mute(true)` / `await voice.mute(false)`          |
| Keypad                        | `await voice.sendDtmf("5")` — one digit per action            |
| Refresh displayed state       | `voice.status()`; `onStatus` supplies updates automatically   |
| Renew an expired idle headset | `await voice.renew()`                                         |
| Disconnect / logout           | `await voice.disconnect()` and server-side session revocation |

Disable new calls while `busy` is true. `startCall()` resolving means the call was
admitted; display the conversation as connected when `call.state` is
`connected`. Local `media: "active"` alone is not proof that the telephone was
answered. The SDK already polls call status; do not add a second polling loop.

## Test in your existing Salesforce dialer

Start with one salesperson and a consenting test recipient:

1. Connect the headset, click your existing Call button, and verify the selected
   telephone receives the call.
2. Check speech in both directions, mute, keypad, and hangup from each end.
3. Place a second call after cleanup. Check that duplicate clicks do not place
   another call.
4. Reload the component and test logout/recovery. Preserve any unknown attempt
   until your backend and Trackly resolve it; do not redial around it.

Trackly's media path has been tested outside Salesforce. Your existing component,
org security settings, and office network still need this acceptance run. Keep
your CRM activity logging and screen-pop logic connected to the returned Trackly
call IDs. Add optional recording, queue, or supervisor controls using their
[Voice API contracts](/api-reference/voice/overview) after confirming the features
enabled for your account.

For quota sizing, expiry, recovery, and optional Apex/LWC/Open CTI setup, use the
[configuration reference](/api-reference/voice/salesforce-configuration).
