Skip to main content

Telephony Soft Phone

Feature NameTelephony
Feature IDCrestApps.OrchardCore.Telephony

The Telephony module adds a provider-agnostic soft phone to Orchard Core. It exposes a SignalR hub that receives call-control requests from the browser and routes them to whichever telephony provider is configured for the tenant. The UI never talks to a provider directly, so the same soft phone works with any provider that implements the telephony abstractions (for example DialPad).

Architecture

The feature is split into three layers so that providers stay decoupled from the UI and the hub:

Browser soft phone (soft-phone.js)
│ SignalR (invoke Dial/Hangup/Hold/...)

TelephonyHub ──► ITelephonyService ──► ITelephonyProviderResolver ──► ITelephonyProvider
(DialPad, ...)
  • CrestApps.OrchardCore.Telephony.Abstractions contains the provider-agnostic contracts: ITelephonyProvider, ITelephonyService, ITelephonyProviderResolver, ITelephonyClient, ITelephonyAuthenticationProvider, ITelephonyAuthenticationService, ITelephonyUserTokenStore, ITelephonyInteractionStore, the request/response and interaction models, TelephonyProviderOptions, TelephonySettings, and TelephonyPermissions. A provider module depends only on this package.
  • CrestApps.OrchardCore.Telephony contains the TelephonyHub, the default service and resolver implementations, the site settings, and the soft phone widget.
  • A provider module (such as DialPad) implements ITelephonyProvider and registers itself as a selectable provider.

The provider contract

A telephony provider implements ITelephonyProvider. The interface covers the common soft phone operations:

OperationMethod
DialDialAsync
Hang upHangupAsync
Hold (pause)HoldAsync
ResumeResumeAsync
Mute / UnmuteMuteAsync / UnmuteAsync
TransferTransferAsync
Merge callsMergeAsync
Send DTMF digitsSendDigitsAsync
Answer / Reject inboundAnswerAsync / RejectAsync
Client bootstrapGetClientCredentialsAsync

Each provider also advertises the operations it supports through the Capabilities property (a TelephonyCapabilities flags value). The soft phone UI uses these flags to show or hide controls.

SignalR hub

The hub is registered with the SignalR module's HubRouteManager:

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
{
HubRouteManager.MapHub<TelephonyHub>(routes);
}

Every hub method runs in its own Orchard Core shell scope and is authorized against the Use the telephony soft phone permission. The hub returns a TelephonyResult to the caller and pushes CallStateChanged, IncomingCall, and ReceiveError events to the connected client through the strongly-typed ITelephonyClient interface.

Site settings

Telephony settings live under Settings → Communication → Telephony and require the Manage telephony settings permission. The screen follows the same multi-provider tab layout as the Orchard Core SMS settings:

  • The Soft Phone tab selects the default provider from the list of enabled providers (as its first option) and configures where the soft phone widget appears.
  • Each enabled provider contributes its own tab (rendered by a display driver in the provider module) where you enable the provider and supply its credentials.

When you enable the only configured provider, it is automatically selected as the default. When you disable the current default provider, the default is cleared and the soft phone is disabled until a new default is chosen.

Soft phone widget

Enable the Telephony Soft Phone feature (CrestApps.OrchardCore.Telephony.SoftPhone) to inject a floating soft phone into the site. Configure where it appears on the Soft Phone tab of the telephony settings:

  • Show the soft phone on the admin dashboard displays the widget on admin pages.
  • Show the soft phone on the front end displays the widget on the website.
  • Accent color controls the widget's button and control colors.

You can enable the soft phone on the admin, the front end, or both. The widget is rendered when its surface is enabled and the current user has the Use the telephony soft phone permission, so the soft phone only appears for authorized users.

Moving and persisting the widget

The soft phone is draggable by its header. Its position and open/closed state are saved to the browser's localStorage, so the widget reappears exactly where you left it after a page reload — and it is restored before the first paint, so there is no flash or jump as the page loads. You can drag the widget anywhere on the screen, including all the way to the right edge and on top of other widgets such as the AI chat widget. By default, when the AI chat widget is also present, the soft phone automatically offsets itself so the two widgets sit side by side instead of overlapping.

Status and call controls

The widget reflects the live connection status reported by the hub and only enables the dial pad and call controls when the provider is available, connected, and authenticated:

  • When no provider is enabled, the header status reads Not Ready instead of a misleading Ready status, the body shows an unavailable message, and the number pad and buttons are hidden.
  • When the provider requires a per-user connection, the widget shows the Connect to provider button (see Authenticating users with a provider).
  • During an active call the main toggle button turns red and switches to a hang-up icon, and the widget exposes mute, hold, transfer, and merge controls based on the provider's capabilities.

Keypad and recent calls tabs

The widget's footer is a tab bar that switches the panel between two views:

  • Keypad – the number field, dial pad, and call controls.
  • Recent – the call history, listing active calls, recent inbound and outbound interactions, and missed calls (highlighted in red with a direction icon). Selecting a recent call dials it again.

The footer is designed to be extensible, so additional tabs can be added in the future. The history is read from the hub's GetInteractions method and is backed by the persisted interaction store described below, so it survives page reloads and is available independently of the provider.

Adding the soft phone to the website

There are two ways to add the soft phone to your site:

  • Automatically – turn on Show the soft phone on the front end (and/or the admin) on the telephony settings. The widget is injected into the layout's Footer zone for authorized users.

  • Manually – render the SoftPhoneWidget shape wherever you want it (for example in a theme layout or a template) and register its resources. This is useful when your theme has no Footer zone or you want precise placement:

    <style asp-name="telephony-soft-phone" at="Head"></style>
    <script asp-name="telephony-soft-phone" at="Foot"></script>
    <script asp-name="telephony-phone-field" at="Foot"></script>

    @await DisplayAsync(await New.SoftPhoneWidget())

The telephony-soft-phone script depends on the signalr script, which the SignalR module adds automatically.

Authenticating users with a provider

Providers use one of two authentication scenarios, and the soft phone adapts automatically:

  • Account-level credentials – the provider authenticates with a shared key configured by an administrator (for example an API token). No per-user step is needed and the dialer is shown immediately. This is the right model for providers such as Twilio.
  • Per-user OAuth 2.0 – the provider requires each user to connect their own account (for example DialPad). When the user is not yet connected, the widget shows a Connect to provider button that starts the OAuth 2.0 authorization code flow in a popup. After the user grants access, the tokens are stored encrypted on the user's account and the dialer is shown. Expired tokens are refreshed automatically when a refresh token is available.

On connection the widget asks the hub for the user's status (GetConnectionStatus). The hub reports whether authentication is required, whether the user is connected, and which authentication scheme the provider uses. The widget's connect experience is extensible: a provider that uses a different per-user scenario can register a handler for its scheme on window.telephonySoftPhone.authHandlers, so new authentication scenarios can be supported without changing the widget.

The OAuth callback URL the soft phone uses is {scheme}://{host}/Telephony/Connect/Callback (prefixed with the tenant URL prefix when one is configured). Register this URL as an allowed redirect URI in your provider's OAuth application.

Phone field click-to-dial

When the soft phone is present on a page, the Phone Field editor and display are enhanced with a dial button that calls the number with the soft phone. The Phone Field views expose a neutral data-phone-dial placeholder, and the soft phone attaches the button to it, so the editors are not overridden. Enable the soft phone on the admin to get click-to-dial while editing content, and on the front end to get it on displayed phone numbers.

Call history and reporting

Every call the hub handles is recorded through ITelephonyInteractionStore and indexed with TelephonyInteractionIndex. Each interaction stores the provider's interaction id, the provider name, the user id and name, the call direction and outcome, the start and end timestamps, and the duration. Because the index covers the interaction id, provider name, user name, and call date, the site keeps a searchable call history for reporting and history tracking that is independent of the provider — the data remains even if the provider integration is later removed.

The soft phone reads this history through the hub's GetInteractions method to render its history panel. Outbound calls placed from the soft phone are recorded automatically. Inbound and missed calls are fully modeled (direction and outcome), but populating them requires the provider module to report inbound events (for example through a provider webhook); the soft phone records the outbound calls it initiates.

Writing a provider

To add a new provider:

  1. Reference CrestApps.OrchardCore.Telephony.Abstractions.

  2. Implement ITelephonyProvider.

  3. Register the provider and an IConfigureOptions<TelephonyProviderOptions> that reflects whether it is enabled based on the tenant settings:

    services.AddTelephonyProviderOptionsConfiguration<MyProviderOptionsConfigurations>();
  4. Add a SiteDisplayDriver<MyProviderSettings> whose SettingsGroupId is TelephonyConstants.SettingsGroupId so the provider gets its own tab on the telephony settings screen.

  5. For per-user authentication, also implement ITelephonyAuthenticationProvider. Declare the AuthenticationScheme (for example TelephonyConstants.AuthenticationSchemes.OAuth2), build the authorization URL, exchange the authorization code for tokens, and refresh tokens. The framework stores the tokens securely on the user's account and supplies them back to your provider through ITelephonyAuthenticationService. Providers that only use a shared account key do not implement this interface.

See the DialPad provider for a complete example.