Integration script c#

Cyrille187

Joined
Jan 7, 2026
Messages
3
Reaction score
0
Hello,


I am looking for a solution for a client.
I would like to know if, with the call scripts, it would be possible to intercept all incoming calls in order to send the following information to a REST API:


  • Caller
  • Call ID
  • Recipient
  • Start time of the call
  • Answer time of the call
  • End time of the call

The goal would then be to allow 3CX’s standard routing and operation to function as usual.
Currently, I have tested the following code which works for a single recipient.

C#:
/*
 * Time Base Call Routing with REST Webhook
 * Calls will be intercepted and redirected to the specified DestinationDN during the following times:
 * Monday to Sunday: 5:30 PM to 7:00 AM
 * Webhook notifications ("Incoming" and "Terminated") are sent to your REST API.
 */

#nullable disable
using CallFlow;
using System;
using System.Threading.Tasks;
using TCX.Configuration;
using TCX.PBXAPI;
using System.Collections.Generic;
using System.Linq;
using CallFlow.CFD;

namespace interceptcall
{
    public class InterceptInboundCall : ScriptBase<InterceptInboundCall>
    {
        // The destination DN to which the call will be redirected
        const string DestinationDN = "1000";

        // URL du webhook REST
        const string WebhookUrl = "http://my-ip:8080/api/webhook/3cx";

        // Définition du planning d'interception
        static readonly Schedule schedule = new Schedule(RuleHoursType.SpecificHours)
        {
            { DayOfWeek.Monday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Monday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Tuesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Tuesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Wednesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Wednesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Thursday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Thursday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Friday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Friday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Saturday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Saturday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Sunday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Sunday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) }
        };

        // Méthode pour envoyer un webhook à ton API REST
        private async Task SendWebhookAsync(string status)
        {
            try
            {
                string callerID = MyCall.Caller?.CallerID ?? "Unknown";
                string calledID = MyCall.DN?.ToString() ?? "Unknown";

                string json = "{"
                    + $"\"CallID\":\"{MyCall.CallID}\","
                    + $"\"From\":\"{callerID}\","
                    + $"\"To\":\"{calledID}\","
                    + $"\"Status\":\"{status}\","
                    + $"\"Timestamp\":\"{DateTime.UtcNow:O}\""
                    + "}";

                using var client = new System.Net.Http.HttpClient();
                var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json");

                var response = await client.PostAsync(WebhookUrl, content);
                MyCall.Trace($"Webhook '{status}' sent. Status: {response.StatusCode}");
            }
            catch (Exception ex)
            {
                MyCall.Error($"Error sending webhook '{status}': {ex.Message}");
            }
        }

        public override async void Start()
        {
            try
            {
                await Task.Run(async () =>
                {
                    try
                    {
                        MyCall.Debug($"Script start delay: {DateTime.UtcNow - MyCall.LastChangeStatus}");
                        MyCall.Debug($"Incoming connection {MyCall} from {MyCall.Caller}");
                        bool intercepted = false;
                        var ps = MyCall.PS as PhoneSystem;

                        //  Webhook "Incoming" at the beginning
                        Task.Run(() => SendWebhookAsync("Incoming"));

                        //  Webhook "Terminated" at the end of the call
                        MyCall.OnTerminated += () =>
                        {
                            Task.Run(() => SendWebhookAsync("Terminated"));
                        };

                        if (MyCall.Caller.DN is ExternalLine externalLine)
                        {
                            var DIDNumber = MyCall.Caller["inbound_did"];
                            var CallerID = MyCall.Caller.CallerID;
                            var currentTime = externalLine.Now(out var utc, out var timezone, out var groupmode);

                            // Intercept calls during the specified schedule
                            if (schedule.IsActiveTime(currentTime))
                            {
                                string[] DIDs = { "*" }; // Allow all DIDs
                                string[] Callers = { "*" }; // Allow all Callers
                                var destination_struct = new DestinationStruct(ps.GetDNByNumber(DestinationDN));

                                if ((DIDs.Contains(DIDNumber) || DIDs.Contains("*")) &&
                                    (Callers.Contains(CallerID) || Callers.Contains("*")))
                                {
                                    try
                                    {
                                        var result = await MyCall.RouteToAsync(destination_struct);
                                        MyCall.Info($"{CallerID} -> {DIDNumber} has been redirected to {DestinationDN} ({result})");
                                        intercepted = true;
                                    }
                                    catch (Exception ex)
                                    {
                                        MyCall.Info($"{CallerID} -> {DIDNumber}: interception failed. '{DestinationDN}' is not reachable: {ex}");
                                    }
                                }
                                else
                                {
                                    MyCall.Info($"{CallerID} -> {DIDNumber}@{currentTime}: Default CallerID/DID based routing will be applied");
                                }
                            }
                        }

                        MyCall.Return(intercepted);
                    }
                    catch (Exception ex)
                    {
                        MyCall.Error($"Script execution failed: {ex}");
                        MyCall.Return(false);
                    }
                });
            }
            catch (Exception ex)
            {
                MyCall.Error($"Task execution failed: {ex}");
                MyCall.Return(false);
            }
        }
    }
}

The API result is as follows:

Result:
info: 3cxJira.Controllers.3cxController[0]

CallID: 1850 | From: 0+0000000000 | To: Wroutepoint.20: ROUTER | Status: Incoming | Timestamp: 01/05/2026 17:32:01 +00:00

CallID: 1850 | From: 0+000000000 | To: Wroutepoint.20: ROUTER | Status: Terminated | Timestamp: 01/05/2026 17:32:22 +00:00



The problem is that there is only one recipient, and I don't know when they pick up the call.
Thank you in advance.
 
Last edited:
If you need to know when someone answers a call, you could configure the web client to call an API you create and pass in the extension, caller number and name. It's built into the client.

Alternatively, you could create a service application (Windows service / Linux daemon) that subscribes to the Call Control API. This service could update your data when a call is answered. It's not "easy" if you are just starting out integrating with 3CX, but it is very feasible and is a server-side process so you avoid the complexities of client-side solutions.
 
  • Like
Reactions: Evolute IT
Hi,
My requirement for creating a Jira ticket is as follows:


  • Know the date and time when the person arrives at the switchboard, along with their phone number.
  • Know the date and time when the call is picked up by an operator.
  • Know the date and time when the call ends.

At the moment, the script captures the number, the date and time of arrival at the switchboard, as well as the end of the call.
What is missing is only the moment when an operator picks up.


It would also be useful to know the CDR_ID of the call.

thanks in advance
 
I don't believe the CDR_ID is exposed in any of the events, but I could be wrong. The CDR_ID is a fairly new piece of data. You can get the History ID, and then back your way into the CDR_ID via the 3CX database export. The least complicated thing is to configure the web client to call your API when the call is answered, but that only gives you who answered the call, and the caller's number and name. It's probably been more than a decade since I looked at this feature but I don't think you can get the History_ID or the CDR_ID through this process -- that would require subscribing to 3CX call events using the Call Control API.
 
Great, thank you for your reply.

I'll try to retrieve the "Answered/Connected" status.

I'm missing this part to answer the client's request.

Cyrille
 

Members Online Now

Forum statistics

Threads
111,831
Messages
589,277
Members
164,660
Latest member
RJenkinsROCK