The Phonebook: Holiday Edition

We all love 3CX — especially the Pro version with all its bells and SIP-based whistles. But there’s one festive little wrinkle: holiday routing with custom prompts. You’d think it’d work out of the box… but it’s more of a “some assembly required” situation.


Fear not.


This guide introduces a powerful 3CX call script that knows when it's a holiday, plays the right greeting, and still handles business — whether that means routing the call, sending it to voicemail, or sending callers off with some seasonal cheer.


Your phone system's about to get a calendar — and yes, it’s smarter than that wall one no one updates.

By Clan Dad July 07, 2025 3 parts

1. The Opening Script

3CX isn’t just a phone system — it’s a full-blown automation powerhouse hiding behind a dial tone. One of its lesser-known superpowers? Call scripting. Instead of just sending callers to a group, extension, or the great voicemail beyond, you can route them through a custom IVR script and do… well, whatever you want.


Want it to check the date? Play a custom holiday greeting? Forward the call, leave a voicemail, or fire off confetti? You got it.


Here’s how to get started:


Go to Integrations → Call Scripts

From there, either:

Create a new custom script, or

Click “Add from store” and choose the basic holiday script as a starting point



Once it’s in your list, you can dive into the script body and start customizing.




🧠 Heads up: These scripts use C#, so some .NET knowledge goes a long way. If “public void” and “DateTime.Now” sound familiar, you’re in good shape. If not — don’t worry — we’ve got the basics covered in the next section.




2. Creation & Assignment

Alright, time to roll up our sleeves and give this script some brains.


Once you’re editing your call script, you’ve got two solid options:


Paste in the full script below (we’ll provide the working version),

OR


Tweak the pre-loaded version from the 3CX script store if you started with the basic holiday template.


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

namespace dummy
{
  //this handler plays holiday prompt as specified for destination
  public class PlayDestinationHolidayPromptBeforeRouting : ScriptBase<PlayDestinationHolidayPromptBeforeRouting>
  {
    //reference implementation of trunk routing.
    DestinationStruct FindDefaultDestination(ExternalLine trunk, string callerID, string DID)
    {
      DestinationStruct retval = new();
      string[] range;
      foreach (var a in trunk.RoutingRules)
      {
        bool match = (a.Conditions.Condition.Type == RuleConditionType.BasedOnDID &&
        (
          a.Data == DID
          || (a.Data.StartsWith('*') && DID.EndsWith(a.Data[1..]))
        ))
        ||
        (
          a.Conditions.Condition.Type == RuleConditionType.BasedOnCallerID &&
          a.Data.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
          .Any
          (x =>
            x == "*"
            || x == callerID
            || x.StartsWith('*') && callerID.EndsWith(x[1..])
            || x.EndsWith('*') && callerID.StartsWith(x[..^1])
            || x.StartsWith('*') && x.EndsWith('*') && callerID.Contains(x[1..^1])
            || ((range = x.Split("-", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).Length == 2 ?
            (range[0].Length == callerID.Length && range[1].Length == callerID.Length) && (range[0].CompareTo(callerID) <= 0 && range[1].CompareTo(callerID) >= 0) : false)
          )
        )
        ||
        (a.Conditions.Condition.Type == RuleConditionType.ForwardAll);

        if (match)
        {
          retval.CopyFrom(a.ForwardDestinations.OfficeHoursDestination);
          break;
        }
      }
      return retval;
    }

    public override async Task<bool> StartAsync()
    {
      if(MyCall.Caller.DN is ExternalLine externalLine && MyCall.IsInbound) //only from trunk
      {
        //get the inbound target
        var defaultDestination = FindDefaultDestination(externalLine, MyCall.Caller.CallerID, MyCall.Caller.CalledNumber).Internal;
        //if the destination is Internal
        if (defaultDestination != null)
        {
          var timeBasedroute = defaultDestination.GetTimeBasedRoutingInfo();

          //if destination is currently "on holiday" and holiday prompt is defined, we play it
          if (timeBasedroute.reason == CallControlAPI.DivertReason.Holiday && !string.IsNullOrWhiteSpace(timeBasedroute.holiday?.HolidayPrompt))
          {
            await MyCall.AssureMedia()
              .ContinueWith(x => MyCall.PlayPrompt(null, [timeBasedroute.holiday?.HolidayPrompt], PlayPromptOptions.Blocked))
              .Unwrap();
          }
        }
      }
      return false;//we always return false to continue default trunk routing procedure.
    }
  }
}


Either way, the goal here is simple:


✔️ Check the holiday schedule

✔️ Play the appropriate greeting if it’s a holiday

✔️ Politely hang up (like a voicemail ninja with seasonal manners)


No complicated logic. No call loops. Just clean, dependable routing — the way dad intended.


In the next section, we'll get the script assigned to the incoming trunk to actually handle our calls.



3. Trunk Assignment

Alright, we’ve built the script, tested the logic, and now it’s time to wire it into the real world — or at least, your 3CX call flow.


This is where the rubber meets the road (or the dial tone meets the destination).


Head over to:


Voice & Chat → select your primary trunk


Once you're in there, click the General tab and locate the “Default Route” option. By default, this is usually set to an extension, a ring group, a queue — maybe even Grandma’s house. That’s fine, but we’re doing something a bit more clever.


Change that default route to:


👉 Call Processing Script


Once you pick that, a dropdown should appear, letting you select your “Holiday Script” from earlier. Pick it like you're choosing your favorite grill spatula — confidently.



From here on out, 3CX will respect the Office Hours → Holiday tab settings. If there's a holiday scheduled and a media file attached, your callers will hear that prompt and then get the respectful boot (a.k.a. hang-up).



And just like that, you did it.


You built a holiday-aware phone system.


You routed calls like a pro.


You made it fax-free and festive.


And me? I’ve never been prouder. 🧔📞🎉