Skip to main content
Start with Connect Your Salesforce Dialer. This reference covers the backend contract and configuration details you may need while adapting an existing integration. Apex, LWC, and Open CTI are options for your current architecture, not prerequisites to build a new dialer.

Backend callbacks

All Trackly paths below are relative to https://api.tracklysms.com/api/v1/voice. Send the server-held key as Authorization: Bearer <Trackly API key>. Reads require voice_calls.read; creation and cleanup require voice_calls.write. Callbacks resolve the parsed response envelope. On failure, reject an error carrying the safe Trackly code; retain HTTP status and Retry-After for your backend’s handling. Honor the supplied AbortSignal where your transport permits, use bounded request deadlines, and disable automatic mutation retries. record is a boolean, defaults to false, and requests recording when supported for the account. customData accepts up to 32 string pairs: keys contain 1–128 characters, cannot start with $ or contain a null character, and values contain at most 1,024 characters. Its compact UTF-8 JSON must fit within 8,192 bytes. Pass either option in the input to startCall(); the SDK forwards it unchanged. GET /browser-sessions/{id} also returns {session} metadata for authorized recovery. Session metadata includes id, agentId, externalSubject, status, expiresAt, and backend. The successful issuance transport contains temporary SIP registration and ICE configuration. Pass that complete envelope to the SDK; metadata-only recovery cannot substitute for it.

Adapter example

These /integration/voice/* paths are example routes in your existing backend, not Trackly endpoints. Implement them using the request table above. authenticatedJson stands for your existing authenticated request helper; it must apply your CSRF/origin protections, return parsed JSON, reject errors with code, and pass through the signal without automatically retrying mutations.

Ownership and retry rules

  • Derive a stable externalSubject, up to 200 characters, from authenticated org and user IDs. Scope mappings by client business when serving multiple clients. Check that every requested session/call belongs to that user; account/key authentication alone does not isolate users sharing a key.
  • Use the same issuing API key for an agent and its sessions. Plan key rotation with Trackly because a new issuer does not inherit existing browser resources.
  • Persist session/call IDs, their owner, attempt keys, original inputs, and unresolved outcomes. Serialize session issuance across tabs and backend workers.
  • Session and call creation require a nonblank Idempotency-Key of at most 128 characters. Changed input with the same key returns 409 idempotency_conflict. A lost response retains the original attempt; do not generate a new key to retry.
  • Authorize caller/contact selection. Verify returned media URLs against Trackly’s approved transport configuration. Preserve approved WSS/ICE settings; do not add browser-selected relays or credentials.
  • Keep credentials in memory and return them with Cache-Control: no-store. Do not log API keys, SIP passwords, TURN credentials, full session envelopes, or SDP. Keep call/session reads uncached because fresh identity checks authorize incoming media and establish cleanup.

Status, expiry, and cleanup

The browser projection uses waiting_agent, dialing_recipient, connected, ending, ended, failed, and outcome_unknown. It differs from the generic Voice API’s individual-leg state names. Use call.state: "connected" for the telephone conversation and busy: false before another attempt. The earlier session/registration expiry blocks new calls. An accepted call keeps its own configured duration limit; renewal does not extend it. Use renew() only while idle. Provide an explicit playback action if audio_playback_blocked occurs. Mute affects outgoing microphone audio only; DTMF accepts one 0–9, *, #, or uppercase A–D digit. An uncertain keypad result is not automatically resent. On a fresh helper, recover() restores metadata and cleanup controls, not live audio or credentials. A discovered session sets recovery_required. Request explicit cleanup using disconnect() and wait for resolution before connecting. An empty metadata result cannot erase an earlier unknown mutation in your backend. Local hangup, HTTP 202, session revocation, and an ended browser audio stream are not individually proof that both call legs have stopped. Preserve busy and unknown states until matching terminal reads and SDK cleanup resolve them. Revoke sessions server-side on logout; browser unload callbacks may never arrive. There is no automatic redial or audio reattachment after reload/network failure.

Quotas and support

The SDK reads an active call approximately once per second. Ten active agents need roughly 600 reads/minute before admission and cleanup. The default key quota is 100 requests/minute and 10,000/hour; arrange an appropriate quota with Trackly before a multi-agent test. Do not add another poller. Inspect X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After on HTTP 429. For support, retain UTC time, application attempt ID, Trackly session/call IDs, safe error code, browser version, and network type. Optional onTiming callbacks report bounded setup durations without credentials or network addresses.

Salesforce packaging

Use the section matching your existing component. These are supported Salesforce configuration mechanisms; the preview bundle still needs testing inside your actual org’s security settings and browser environment.

Existing LWC component

Upload the downloaded JavaScript bundle as a Static Resource, then load it once using loadScript from lightning/platformResourceLoader. Use your component’s audio element and existing handlers. Salesforce documents this in Use Third-Party JavaScript Libraries.
This is a fragment inside your existing component class. Guard repeated renders so it loads and initializes once. LWS supplies a sandboxed window; load and use the SDK in the same component namespace. Keep LWS/Locker enabled and validate WebSocket, microphone, and audio behavior in the org. See third-party library considerations for LWS.

Existing Visualforce or Open CTI wrapper

Load the bundle through your current approved static-resource/application setup and retain your existing Salesforce screen-pop and activity-logging integration. The SDK supplies calling behavior; it does not register a Salesforce Call Center. Salesforce lists Open CTI as maintenance-only, with retirement scheduled for February 2028 and restrictions on newly created Agentforce Service orgs. Reusing an existing eligible wrapper is a separate decision from creating a new one. See the Open CTI support policy.

Media permissions

Approve Trackly’s exact wss:// media origin under the applicable connect-src policy, and your backend’s HTTPS origin if the browser calls it directly. An HTTPS entry does not approve a different WSS URL. Microphone permission is separate: embedded components need permission through the parent/iframe policy as well as the browser’s user prompt. Test both the selected microphone and output device. See Salesforce’s CSP guide.

Apex instead of an existing application backend

If your current dialer already uses Apex for server calls, you can keep it. Configure a modern Named Credential with a Custom External Credential and a Named Principal for the Trackly API key. Supply the custom Bearer header server-side; grant principal access through your permission set. The browser must not receive that persistent key. Salesforce documents custom API-key headers. Implement the same user ownership and durable attempt rules above. Do not mark credential or call-control methods cacheable. A cancelled browser request cannot recall an Apex callout already sent to Trackly; preserve the attempt for recovery. Check Apex limits at the intended per-agent polling volume before rollout.