What's New in IPWorks VoIP 2026


Requirements: IPWorks VoIP

Introduction

We are pleased to announce the Beta release of IPWorks VoIP 2026. This release adds Busy Lamp Field (BLF) support to the IPPhone component, along with several improvements to recording and audio streaming. This article summarizes the new features below, and includes technical details on the accompanying API changes.

If you are upgrading from a previous version, please see the API Changes section below for details on configuration settings that have been removed or promoted to properties.

Contents

Busy Lamp Field (BLF) Support

The IPPhone component can now monitor the call/dialog state of other users, commonly known as Busy Lamp Field (BLF), using the SubscribeUserStatus method. This method takes the username or extension of the user to monitor, as well as a requested subscription duration (in seconds), and returns a unique SubscriptionId. Once subscribed, the component will automatically refresh the subscription for as long as it remains active, so SubscribeUserStatus only needs to be called once per monitored user.

Note that the component only acts as a watcher in this exchange: it can monitor the dialog state of other users, but does not itself act as a notifier, and cannot be monitored by other watchers through this feature. Tracking a user's dialog state and generating the corresponding notifications is the responsibility of the SIP server. The component simply subscribes to and reports on the updates it receives.

As the states of the monitored user's dialogs change (e.g., when a call is placed, answered, or ended), the UserStatusChanged event will fire, reporting the affected DialogId, its Direction, and its current State. Monitoring a user can be stopped at any time by passing the associated SubscriptionId to the UnsubscribeUserStatus method. For example:

ipphone.OnUserStatusChanged += (o, e) => { Console.WriteLine(e.ObservedUser + " (" + e.DialogId + "): " + e.State); };
string subscriptionId = ipphone.SubscribeUserStatus("1001", 7200); ... ipphone.UnsubscribeUserStatus(subscriptionId);

The Direction parameter indicates, when known, whether the monitored user initiated or received the call associated with the dialog:

  • "" (empty string): The direction of the dialog is not known or not specified by the server.
  • "initiator": The monitored user initiated the dialog (i.e., placed an outgoing call).
  • "recipient": The monitored user is the recipient of the dialog (i.e., received an incoming call).

The State parameter indicates the current state of the dialog:

  • "trying": The dialog has been initiated, but the far end has not yet been contacted or has not yet responded.
  • "proceeding": The dialog is being routed to the far end; a provisional response, other than "early", has been received.
  • "early": The far end is being alerted, or early media (such as ringback) is available.
  • "confirmed": The dialog has been established, and the call is active.
  • "terminated": The dialog has ended. Note this only indicates that this particular dialog has ended. The associated subscription remains active until UnsubscribeUserStatus is called, or until the server ends the subscription.

For an overview of registration and other IPPhone basics, see the Getting Started with IPPhone guide.

Recording and Audio Streaming Updates

This release includes several improvements to recording and audio streaming, making it easier to work with raw audio bytes, for example, when sending a call's audio to an AI agent such as OpenAI's Realtime API.

Dynamic Recording

The EnableDynamicRecording config, which controls whether StartRecording streams recorded audio in real time via the Record event, is now enabled by default (disabled in previous versions). This means that calling StartRecording with an empty FileName parameter will, by default, immediately begin firing Record repeatedly as audio is captured, rather than waiting until the recording finishes. To restore the previous behavior of buffering all audio until the recording finishes, set this config to false before calling StartRecording:

ipphone.Config("EnableDynamicRecording=false"); ipphone.StartRecording("callId", "");

Record Direction

The Record event now includes a Direction parameter, indicating whether a given chunk of recorded audio is incoming (received from the remote party) or outgoing (sent from the local client). A value of 0 indicates outgoing audio, and a value of 1 indicates incoming audio. This parameter should only be interpreted while dynamic recording is enabled (see Dynamic Recording above). When disabled, Record instead contains the full mixed audio of the call. For example:

ipphone.StartRecording("callId", "");
ipphone.OnRecord += (o, e) => { string direction = e.Direction == 1 ? "Incoming" : "Outgoing"; Console.WriteLine($"Received {e.RecordedDataB.Length} bytes of {direction} audio"); };

Audio Encoding

The AudioEncoding config is now available directly as a property. This property specifies the format of raw audio bytes passed to and from the component when dynamically recording a call (via the Record event) and when calling PlayBytes. Possible values are:

  • -1 (default): PCM 8 kHz 16-bit
  • 0: PCMU (G.711 u-law)
  • 8: PCMA (G.711 a-law)

For example:

ipphone.AudioEncoding = 0; // PCMU
// First call to PlayBytes for specified call ipphone.PlayBytes(callId, pcmuByteArray, true);

Note that this property is applied per call, the first time audio is played or recorded for that call. Changing this property after a call has already started playing or recording audio will not affect that call. As such, this property should be set prior to using StartRecording and PlayBytes for any given call.

API Changes

The following configuration settings have been removed in this release. Projects referencing these configs via Config should be updated accordingly:

  • The AudioDirection config has been removed. The direction of dynamically recorded audio is now reported directly via the new Direction parameter of the Record event.
  • The AudioEncoding config has been removed, and is now available as the AudioEncoding property.
  • The Domain has been removed, and is now available as the Domain property. This property specifies the SIP domain associated with the account, which may differ from the Server used to reach the provider (e.g., some providers route registration/media through one host but expect SIP URIs to reference a separate domain). If left unspecified, Server is used as the default domain. For example:

ipphone.Server = "sip.example.com"; ipphone.Domain = "example.com"; // Optional, only needed if it differs from the server

Additionally, the default value of the EnableDynamicRecording config has changed from False to True. Projects that relied on the previous default (buffering all recorded audio until the recording finishes) should explicitly set this config to False.

We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@nsoftware.com.