Termination Attempt

Introduction

An SIP incall Lua script may request that an attempt be made to terminate to a B-Leg using an outbound SIP INVITE Request.

If any internal Media Server or external SRF is currently connected to the A-Leg as a result of playing previous announcements, then it will be disconnected prior to the B-Leg termination attempt.

If the B-Leg is successfully established, then the SIP framework will connect it to the A-Leg using either SIP INVITE Response (200 OK) or SIP re-INVITE Request.

There are three distinct modes for a Termination:

In the simplest case (no monitoring, no charging) the termination is attempted with four possible outcomes.

  1. The A-Leg abandons the call. The Lua script can no longer interact with the call.
  2. The B-Leg is answered. The Lua script can no longer interact with the call.
  3. The B-Leg does not answer. The Lua script resumes control of the call.
  4. A SIP processing error occurs. The Lua script can no longer interact with the call.

If Monitoring is requested, then the second outcome is different:

  1. The B-Leg is answered. The Lua script must read monitor reports for the call.

If Charging is requested, then the second outcome is different:

  1. The B-Leg is answered. The Lua script must read charge reports for the call and grant extensions.

In this case where a Monitored or Charged call is answed by the B-Leg, the service logic may regain control of an answered call, i.e. in the case where the B-Leg is torn down but the A-Leg remains.

If the call is not Monitored or Charged then the service logic has no further involvement in a call after it is answered.

The LhoSipLuaService Termination API

.termination_attempt [Asynchronous]

This method provides full access to the Termination (Attempt & Monitored) mechanism and is provided for use by custom service developers. In the majority of scenarios, one of the subsequently-described convenience methods should be sufficient and preferable. However, this full-featured alternative is provided for use by complex or non-standard services.

NOTE: If this method returns result.answered == true and result.monitored == true then your service logic must invoke sip_incall_api.monitor_check until the call is no longer monitored.

NOTE: If this method returns result.answered == true and result.charged == true then your service logic must alternately invoke sip_incall_api.charge_check and sip_incall_api.charge_extend until the call is no longer charged.

This method takes a single details parameter which is a LUA table with the following structure:

Field Type Description
details Object [Required] The detailed SIP parameters for the message.
.address_digits (+)Hex String [Required] The digits part of the new To address for the B-Leg.
The protocol and domain parts of the address will be determined automatically.
.no_answer_timeout Integer Specify how long the B-Leg SIP INVITE Request will be permitted to try before we use SIP CANCEL.
(Default = use the default_no_answer_secs configured on the Service).
.extra_headers Object Additional user headers to add for this outbound B-Leg SIP INVITE.
The table entry key is the header name.
Each table entry value is a list (array) of header value strings.
(Default = do not add extra outbound SIP INVITE user headers).
.max_call_secs Integer Specifies the maximum permitted call duration in seconds.
The LhoSipApp may enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).
.monitored 1 Set this value to indicate that this is a "Monitored" call attempt.
The call will have SIP re-INVITE activity tests sent to check that the call is still active.
If this call attempt is answered, then the service logic must use the monitor_check method to monitor the call until it completes.
(Default = the call is not monitored).
.monitor_interval_secs Integer Request the desired interval in seconds between monitor reports.
The LhoSipApp may enforce minimum and maximum bounds for this value.
Valid only when monitored = 1.
(Default = use the monitor_interval_secs configured on the LhoSipApp).
.charged 1 Set this value to indicate that this is a "Charged" call attempt.
The call will have SIP re-INVITE activity tests sent to check that the call is still active.
If this call attempt is answered, then the service logic must use the charge_check and charge_extend methods to authorize extensions on the charged call until it completes.
(Default = the call is not charged).
.grant_secs Integer [Required] Request the initial granted talk-time.
The LhoSipApp may enforce minimum and maximum bounds for this value.
Valid only when charged = 1.

The termination_attempt method returns a table structure indicating the result of the termination attempt.

Attribute Type Description
.controlled Boolean [Required] Is this call still controlled at all right now.
This value being true indicates that (at least some) telephony API actions can be performed by service logic.
Note that in some cases the available control actions may be limited.
.answered Boolean Was this call answered.
This value being true indicates that the A-Leg to B-Leg setup was successfully completed.
.monitored Boolean Is this call monitored.
This value will be true when "Monitored" termination was requested, and answered = true.
.charged Boolean Is this call charged.
This value will be true when "Charged" termination was requested, and answered = true.
.decline_ok Boolean Can the service logic still use the .decline method?
This field is present if and only if .controlled is true.
This field will be true if we have not yet sent a 2XX or 300-699 Response code.
.code Integer An SIP Response final code in the range 200-699 associated with the B-Leg termination attempt.
This value is present only when the B-Leg termination attempt received a final SIP INVITE Response code.
.reason No Route / Declined / No Answer / Answered / Abandoned [Required] A descriptive string for how the termination attempt ended.
.max_call_secs Integer Specifies the actual maximum permitted call duration in seconds, in the case when a call is answered.
The LhoSipApp will apply default, minimum, and maximum bounds for this value, so it may differ from requested.
.monitor_interval_secs Integer Confirms the actual monitored call activity test interval, in the case where a monitored call is answered.
The LhoSipApp will apply default, minimum, and maximum bounds for this value, so it may differ from requested.
.grant_secs Integer Confirms the actual first charge period granted, in the case where a charged call is answered.
The LhoSipApp will apply minimum, and maximum bounds for this value, so it may differ from requested.

If the B-Leg is not answered, the result controlled/answered/code/reason are one of the following sub-cases:

If the B-Leg is answered for a call attempt (not monitored, not charged) the result will be:

If the B-Leg is answered for a call attempt (monitored) the result will be:

If the B-Leg is answered for a call attempt (charged) the result will be:

Example (connection attempt to called party with a prefix, 60 second no-answer timeout, custom header):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.termination_attempt ({
    address_digits = '1703' .. sip_incall.normalised_called_party,
    no_answer_timeout = 60,
    extra_headers = {
      'ChargeRef' = { 'XX-001234' }   -- Header values must be contained in a list.
    }
})

if (result.answered) then
    n2svcd.notice ("CALL ANSWERED = YES")

else
    if (result.controlled) then
        n2svcd.notice ('Controlled after Reason = %s', result.reason)
        if (result.decline_ok) then
            incall_api.decline (633)

        else
            incall_api.hangup ()
        end

    else
        n2svcd.notice ('Control Lost after Reason = %s', result.reason)
    end
end

return 

Example (connection monitored attempt to called party with a prefix, 10 minute maximum talk time, 60 second interval):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.termination_attempt ({
    monitored = true,
    address_digits = '1703' .. sip_incall.normalised_called_party,
    max_call_secs = 600,
    monitor_interval_secs = 60
})

-- When a monitored call is answered, we must sweep for monitor events.
if (result.answered) then
    repeat
        result = sip_incall_api.monitor_check ()
    until (not result.monitored)
end

return 

Example (connection monitored attempt to called party with a prefix, 10 minute maximum talk time, 60 + 30 + 30 + end):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.termination_attempt ({
    charged = true,
    address_digits = '1703' .. sip_incall.normalised_called_party,
    max_call_secs = 600,
    grant_secs = 60
})

-- When a charged call is answered, we must sweep for charge events and extend.
-- We grant 30 + 30 then no more
--
if (result.answered) then

    local ngrants = 0
    while (true) do
        result = sip_incall_api.charge_check ()
        if (not result.charged) then break end

        ngrants = ngrants + 1
        if (ngrants <= 2) then
            sip_incall.charge_extend (30)

        else 
            sip_incall.hangup ()
            break
        end
    end
end

return 

.connect_attempt [Asynchronous]

This helper method provides a flattened-argument version of the termination_attempt method.

The connect_attempt method takes the following arguments:

Field Type Description
address_digits (+)Hex String [Required] The digits part of the new To address for the B-Leg.
The protocol and domain parts of the address will be determined automatically.
no_answer_timeout Integer Specify how long the B-Leg SIP INVITE Request will be permitted to try before we use SIP CANCEL.
(Default = use the default_no_answer_secs configured on the Service).
max_call_secs Integer Specifies the maximum permitted call duration in seconds.
The LhoSipApp may enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).

The connect_attempt method returns the same result structure as termination_attempt.

Example (connect to called party with a prefix, 60 second no-answer timeout):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.connect_attempt ('1703' .. sip_incall.normalised_called_party, 60)

if (result.answered) then
    n2svcd.notice ("CALL ANSWERED = YES")
end

return 

.connect_monitored [Asynchronous]

This helper method provides a flattened-argument version of the termination_attempt method with “Monitored” call mode enabled.

NOTE: If this method returns result.answered == true then your service logic must invoke sip_incall_api.monitor_check until the call is no longer monitored or the logic applies a forced call-end by invoking the hangup method or ending the script.

The connect_monitored method takes the following arguments:

Field Type Description
address_digits (+)Hex String [Required] The digits part of the new To address for the B-Leg.
The protocol and domain parts of the address will be determined automatically.
no_answer_timeout Integer Specify how long the B-Leg SIP INVITE Request will be permitted to try before we use SIP CANCEL.
(Default = use the default_no_answer_secs configured on the Service).
monitor_interval_secs Integer Request the desired interval in seconds between monitor reports.
The LhoSipApp may enforce minimum and maximum bounds for this value.
(Default = use the monitor_interval_secs configured on the LhoSipApp).
max_call_secs Integer Specifies the maximum permitted call duration in seconds.
The LhoSipApp may enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).

The connect_monitored method returns the same result structure as termination_attempt.

Example (connect to called party with a prefix, 60 second monitor intervals):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = sip_incall_api.connect_monitored ("902112233", 10, 3, 20)

if (result.answered) then
    n2svcd.notice ("CALL ANSWERED = YES")

    print ("Actual monitor_interval_secs = " .. tostring (result.monitor_interval_secs))
    print ("Actual max_call_secs = " .. tostring (result.max_call_secs))

    -- When a monitored call is answered, we must sweep for monitor events.
    repeat
        result = sip_incall_api.monitor_check ()
    until (not result.monitored)

elseif (result.decline_ok) then
    sip_incall_api.decline (400)
end

return 

.connect_charged [Asynchronous]

This helper method provides a flattened-argument version of the termination_attempt method with “Charged” call mode enabled.

NOTE: If this method returns result.answered == true then your service logic must invoke sip_incall_api.charge_check and sip_incall.charge_extend alternately until the call is no longer charged or the logic applies a forced call-end by invoking the hangup method or ending the script.

The connect_charged method takes the following arguments:

Field Type Description
address_digits (+)Hex String [Required] The digits part of the new To address for the B-Leg.
The protocol and domain parts of the address will be determined automatically.
no_answer_timeout Integer Specify how long the B-Leg SIP INVITE Request will be permitted to try before we use SIP CANCEL.
(Default = use the default_no_answer_secs configured on the Service).
grant_secs Integer [Required] Request the initial granted talk-time.
The LhoSipApp may enforce minimum and maximum bounds for this value.
max_call_secs Integer Specifies the maximum permitted call duration in seconds.
The LhoSipApp may enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).

The connect_charged method returns the same result structure as termination_attempt.

Example (connect to called party with a prefix, grant 30 seconds indefinitely):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = sip_incall_api.connect_charged ("902112233", nil, 30)
if (result.answered) then
    while (true) do
        result = sip_incall_api.charge_check ()
        if (not result.charged) then break end
        sip_incall.charge_extend (30)
    end
end

return 

.monitor_check [Asynchronous]

This monitor check method must be called by any script using monitored calls, after a monitored call attempt has been answered.

The logic must repeat calls to result = sip_incall_api.monitor_check until not result.monitored.

The script may perform other protocol actions prior to each invocation of monitor_check. These may include asynchronous actions, e.g. Diameter client or REST client actions via the corresponding agent.

See also the subsequent sections related to follow-on telephony actions, and to service-initiated hangup during monitored calls for further discussion of how the monitor_check method interacts with other telephony methods.

The monitor_check method takes no arguments.

The monitor_check method returns a table with the following structure:

Attribute Type Description
.controlled Boolean [Required] Is this call still controlled at all right now.
This value being true indicates that (at least some) telephony API actions can be performed by service logic.
Note that in some cases the available control actions may be limited.
.monitored Boolean [Required] Is this call still monitored.
When this value returns true it indicates that the A-Leg to B-Leg talk-time is still in progress, and the total talk time has reached the sum of all monitoring report intervals to-date.
.monitored_secs Integer What is the total talk time confirmed by this and all previous reports.
This value is present only when .monitored = true.
.talk_dsm Integer Returns the measured talk-time in deci-seconds for the entire talk period.
This value is present only when .monitored is not true.

If the call successfully completes the entire monitoring interval with both legs still connected, then the return value is:

If the B-Leg is terminated but the A-Leg is still available, then follow-on telephony is possible, and the return is:

If the A-Leg is terminated, then the B-Leg will also be terminated and the return is:

.charge_check [Asynchronous]

This charge check method must be called by any script using charged calls, after the charged call attempt has initially been answered, and then subsequently after each call to charge_extend.

The script may perform other protocol actions prior to each invocation of charge_check. These may include asynchronous actions, e.g. Diameter client or REST client actions via the corresponding agent.

See also the subsequent sections related to follow-on telephony actions, and to service-initiated hangup during monitored calls for further discussion of how the charge_check method interacts with other telephony methods.

The charge_check method takes no arguments.

The charge_check method returns a table with the following structure:

Attribute Type Description
.controlled Boolean [Required] Is this call still controlled at all right now.
This value being true indicates that (at least some) telephony API actions can be performed by service logic.
Note that in some cases the available control actions may be limited.
.charged Boolean [Required] Is this call still charged.
When this value returns true it indicates that the A-Leg to B-Leg talk-time is still in progress, and the total talk time has reached the sum of the initial grant and all subsequent grant intervals to-date.
.charged_secs Integer What is the total talk time confirmed by this and all previous reports.
This value is present only when .charged = true.
.talk_dsm Integer Returns the measured talk-time in deci-seconds for the entire talk period.
This value is present only when .charged is not true.

If the call successfully completes the total granted time with both legs still connected, then the return value is:

If the B-Leg is terminated but the A-Leg is still available, then follow-on telephony is possible, and the return is:

If the A-Leg is terminated, then the B-Leg will also be terminated and the return is:

.charge_extend [Synchronous]

This charge grant method must be called by any script using charged calls, whenever the charge_check method returns result.charged = true and the service decides to grant a subsequent talk-time extension.

If the service does not wish to grant a talk-time extension, then it should invoke the hangup method or end the script. This must be done before the service logic timer expires.

The script may perform other protocol actions prior to each invocation of charge_extend. These may include asynchronous actions, e.g. Diameter client or REST client actions via the corresponding agent.

See also the subsequent sections related to follow-on telephony actions, and to service-initiated hangup during monitored calls for further discussion of how the charge_extend method interacts with other telephony methods.

The charge_extend method takes the following arguments:

Field Type Description
grant_secs Integer [Required] Request the additional granted talk-time interval in seconds.
The LhoSipApp may enforce minimum and maximum bounds for this value.

The charge_extend method returns true.

Limited Telephony within Monitor/Charge

The termination_attempt, monitor_check, and charge_check methods can return:

This is a limited form of telephony control. The service logic can only perform some very specific functions while the monitored/charged call is in progress, specifically:

After using the hangup method, you should not call monitor_check, charge_check, or charge_extend. The call is now over and there is no additional information which can be provided.

Follow-On Telephony after Monitor/Charge

The monitor_check and charge_check methods can return:

This occurs when the B-Leg of the monitored/charged call ends, but the A-Leg is still available.

At this time, all of the standard telephony methods are now available, including follow-on interaction and/or B-Leg termination.

The exception is the decline method (including the redirect wrapper method). This method can never be used after any termination attempt is performed.

Slow Service Logic

All service logic must respond promptly, within the service_logic_ms timer (default = 2500 milliseconds) configured for the LhoSipApp.

If the service logic response is not received within this time whenever it is expected, then the LhoSipApp will assume that call control has been abandoned, and will terminate the in-progress call with the loss of all legs.

Constants

The following constants are defined on the returned sip_incall_service object.

sip_incall_service.REASON_NO_ROUTE = "No Route"        -- B-Party RSF (we have no route)
sip_incall_service.REASON_DECLINED = "Declined"        -- B-Party Busy (and other explicit decline)
sip_incall_service.REASON_NO_ANSWER = "No Answer"      -- B-Party Time-Out
sip_incall_service.REASON_ANSWERED = "Answered"        -- B-Party Answer
sip_incall_service.REASON_ABANDONED = "Abandoned"      -- A-Party Hangup

sip_incall_service.EARLY_MEDIA_REQUIRE = "require"
sip_incall_service.EARLY_MEDIA_PREFER = "prefer"
sip_incall_service.EARLY_MEDIA_ALLOW = "allow"
sip_incall_service.EARLY_MEDIA_NEVER = "never"