Build Your Connector

Starting from the demo connector, replace Open CTI adapter logic with Salesforce Voice connector methods and events.

Start From the Demo Connector 

In the cloned demo connector location, open src/main/connector.js. This file already has VendorConnector and TelephonyConnector fully scaffolded. Your migration task is to replace the mock backend calls in each method with calls to your real telephony system.

DirectionWhat It MeansExamples
Your connector calls SalesforceYour connector publishes an event; Salesforce reacts.publishEvent(CALL_STARTED), publishEvent(HANGUP)
Salesforce calls your connectorYour connector implements a method and returns a promise; Salesforce invokes it when an in-org event occurs. Return a promise with the result; do not call publishEvent() yourself from inside the method.acceptCall(), endCall(), setAgentStatus(), superviseCall()

Use AI Assistants to Accelerate the Rewrite 

After cloning the demo connector, consider using AI coding assistants for the mechanical translation of your Open CTI adapter logic into Salesforce Voice connector methods. Because patterns are systematic and each opencti.* call maps to a specific connector method or event, AI agents are helpful for this migration.

Although you can use an AI assistant for certain straightforward tasks, such as translating opencti calls to connector methods, writing unit tests, or generating SOQL for reporting, other tasks require manual insight. Make sure that you check or implement these items yourself:

  • Whether to use enqueueNextState correctly. The timing is subtle.
  • Multiparty event sequencing: AI can generate the call paths, but you should validate each pattern (blind transfer, warm transfer, consult, add caller) manually.
  • Deciding which custom fields belong on VoiceCall vs. a child object: This is a domain decision, not a coding decision.
  • Final Flow construction: The AI gives you the spec; Flow Builder is where you implement.
  • Auth / SSO into your backend: This should be reviewed by a human; AI-generated auth code is a common source of security issues.

Here are sample prompts to start migrating your adapter.

  1. Feed the assistant your Open CTI adapter and related information.

    Example prompt
    1I'm migrating an Open CTI adapter to a Salesforce Voice Partner Telephony connector.
    2Here is my current Open CTI adapter: [paste or attach]
    3SDK: https://github.com/salesforce/scv-connector-base
    4Demo connector (my starting point): https://github.com/salesforce-misc/byo-demo-connector
    5Developer guide: https://developer.salesforce.com/docs/atlas.en-us.voice_pt_developer_guide.meta/voice_pt_developer_guide/voice_pt_intro.htm
    6For each opencti.* API call in my adapter, tell me:
    71. The Salesforce Voice connector equivalent method or event
    82. Where in byo-demo-connector it's already stubbed
    93. What I need to replace

    If your AI tool isn’t able to parse the Salesforce Voice with Partner Telephony Developer Guide, point it to the latest PDF version (available from the guide overview page).

    Tip

  2. Generate connector methods one at a time. Don’t ask for the whole file at once. Migrate method by method, in priority order.

    Example prompt
    1In my Open CTI adapter, call acceptance looks like this:
    2[paste your existing code]
    3Generate the Salesforce Voice TelephonyConnector.acceptCall() implementation
    4that calls my backend at [your endpoint], using the PhoneCall and CallResult
    5types from scv-connector-base. Return a Promise<CallResult> — do not call
    6publishEvent() from inside acceptCall().
  3. Repeat for endCall(), dial(), hold(), mute(), setAgentStatus(), wrapUpCall(), etc.

  4. Generate unit tests against the SDK’s test fixtures.

    Example prompt
    1Generate Jest unit tests for my acceptCall() implementation using the
    2test patterns in byo-demo-connector/src/main/__tests__/. Mock the backend
    3with the same vendor-sdk.js mock pattern the demo uses.
  5. Validate with the CCaaS.html simulator. After generating code, test against the demo connector’s mock backend before connecting your real backend. The simulator catches event-ordering bugs early, such as CALL_CONNECTED before CALL_STARTED, missing voiceCallId, and other rejections the base connector enforces.

    1cd byo-demo-connector
    2npm run serve
    3# Open the printed CCaaS.html URL in a browser tab.
    4# Simulate inbound/outbound calls, hangups, and transfers without a real PBX.
  6. Generate Omni-Channel Flow specifications (build in Flow Builder). For Flows (screen pop, post-call automation), AI tools generate Flow specifications and step-by-step build instructions, not deployable Flow XML. Use the AI output as a buildable spec. Build the actual Flow in Flow Builder.

Methods to Replace, in Priority Order 

Priority 1 (required for basic calls):

1acceptCall(); // replace mock with your accept API → Promise<CallResult>
2declineCall(); // replace mock with your decline API → Promise<CallResult>
3endCall(); // replace mock with your hangup API → Promise<HangupResult>
4dial(); // replace mock with your outbound dial → Promise<CallResult>
5getActiveCalls(); // replace mock with your call state query → Promise<ActiveCallsResult>

Priority 2 (required for complete experience):

1hold() / resume();
2mute() / unmute();
3setAgentStatus() / getAgentStatus();
4wrapUpCall();

Priority 3 (advanced features):

1addParticipant(); // transfers and consults
2superviseCall(); // supervisor listen-in
3supervisorBargeIn();
4getVoiceCapabilities() / getSharedCapabilities();

Publish Call Events 

When your telephony system fires events, translate them using the pattern that’s already in the demo connector’s event publisher:

1// Inbound call ringing → publish to Salesforce Voice
2publishEvent({
3  eventType: Constants.VOICE_EVENT_TYPE.CALL_STARTED,
4  payload: {
5    call: new PhoneCall({
6      callId: vendorCallId,
7      callType: Constants.CALL_TYPE.INBOUND,
8      state: Constants.CALL_STATE.RINGING,
9      phoneNumber: callerNumber,
10      callAttributes: new PhoneCallAttributes({
11        voiceCallId: sfVoiceCallId, // ← 0LQ... Salesforce VoiceCall record ID
12        participantType: Constants.PARTICIPANT_TYPE.INITIAL_CALLER,
13      }),
14      callInfo: new CallInfo({ isOnHold: false, acceptEnabled: true, declineEnabled: true }),
15    }),
16  },
17});

voiceCallId in callAttributes must be the Salesforce VoiceCall record ID (0LQ…). This is the binding between your call and everything Salesforce does (AgentWork creation, supervisor view, reporting).

Important