Photo by Tima Miroshnichenko: https://www.pexels.com/photo/elderly-man-in-santa-costume-using-a-laptop-6021586/
It’s fast approaching Christmas and for many offices and families that means Secret Santa! For those who aren’t familiar, in Secret Santa every member of the group is assigned a giftee they are to be Santa for, and everyone agrees a maximum spend on the gifts they can get. It works well in offices, and my family started doing this a few years ago since we were all adults and basically just transferring gift cards between us. Now we have a single person to buy for, a simple limit, and a lot less stress.
As the family’s IT support, I was the one tasked with setting the Secret Santa up and whilst services exist out there to do this in a secretive way, they all require giving away some data which I wasn’t comfortable with (nor did I want to have to support elderly relatives having to find the email and click the link and so on). For the last couple of years, I have stuck the names into a random assignment tool, pressed go and then messaged people individually, however this was again limited as it meant I knew who everyone had got including myself. So this year I thought I would I would come up with a solution in Salesforce that allows me to do the Secret Santa assignment and then send out a text message to the participants with their assignee, as well as storing it in Salesforce. I am going to store the assignments just in case, as I can almost guarantee at least one family member will forget who they were assigned, and if you were running this for a company it could be handy to have as well. Anyway, on with the solution!
Elfing ourselves out with some new fields
To manage all this I will be using Campaigns and Campaign Members. Why? They are a handy way of grouping multiple people together, if the members are (for example) Lead records, it means that a single person could be in multiple Secret Santa parties (this happened to me at one job where I was in the groups for the dev team and the office group I was in). I added 2 new fields on to the Campaign Member - Giftee_Name__c and Giftee_Id__c which I will populate for use in sending the messages and storing who the giftee was. That’s all the additional stuff we need there.
Gift-wrapping some logic in an invocable method
I spent a few hours trying to do a Flow only solution but could not get it to allow me to resolve excluding people from the list properly (there is unfortunately still no Map style option in Flow) so I switched it up and created the following invocable Apex:
public with sharing class SecretSantaInvocable {
@InvocableMethod(label='Assign Secret Santas' description='Assign Secret Santa for the members of this Campaign' category='Secret Santa')
public static List<List<CampaignMember>> assignSecretSantas(List<Id> campaignIds) {
List<List<CampaignMember>> results = new List<List<CampaignMember>>();
Map<Id, List<CampaignMember>> campaignMembersByCampaignId = new Map<Id, List<CampaignMember>>();
for(CampaignMember cm : [SELECT Id, FirstName, LastName, LeadId, MobilePhone, Giftee_Name__c, Giftee_Id__c, CampaignId FROM CampaignMember WHERE CampaignId in :campaignIds]) {
if(!campaignMembersByCampaignId.containsKey(cm.CampaignId)) {
campaignMembersByCampaignId.put(cm.CampaignId, new List<CampaignMember>());
}
campaignMembersByCampaignId.get(cm.CampaignId).add(cm);
}
for(List<CampaignMember> members : campaignMembersByCampaignId.values()) {
List<CampaignMember> returnMembers = new List<CampaignMember>();
Set<Id> assignedGiftees = new Set<Id>();
for(CampaignMember santa : members) {
List<CampaignMember> validGiftees = getValidGiftees(members, assignedGiftees, santa.Id);
CampaignMember randomGiftee = selectRandomGiftee(validGiftees);
santa.Giftee_Id__c = randomGiftee.Id;
santa.Giftee_Name__c = randomGiftee.FirstName + ' ' + randomGiftee.LastName;
returnMembers.add(santa);
assignedGiftees.add(randomGiftee.Id);
}
results.add(returnMembers);
}
return results;
}
private static List<CampaignMember> getValidGiftees(List<CampaignMember> allGiftees, Set<Id> assignedGiftees, Id santaId) {
List<CampaignMember> returnList = new List<CampaignMember>();
for(CampaignMember giftee : allGiftees) {
if(!assignedGiftees.contains(giftee.Id) && giftee.Id != santaId) {
returnList.add(giftee);
}
}
return returnList;
}
private static CampaignMember selectRandomGiftee(List<CampaignMember> giftees) {
Integer randomIndex = (Math.random() * giftees.size()).intValue();
return giftees[randomIndex];
}
}
A couple of things off the bat, because it is an invocable method I need to be able to handle lists of stuff rather than simply individual items so we do initially retrieve our Campaign Members using the Campaign Ids we have been passed and put them into a map. Once we have the lists, we loop over the list of members for each campaign and do the following:
- Retrieve all the valid giftees we can give to - that is someone who isn’t already receiving a gift and is not ourselves (we cannot self-gift)
- Find a random giftee from that list
- Update the fields on our santa we are working as
- Remove that giftee from the possible giftees by adding their Id to the
assignedGifteesset
We then rinse and repeat until we have a list that has been processed and we return that list of Campaign Members for our Flow to use. A couple of other quick thoughts; I have written a lot more TypeScript and JavaScript than Apex recently and missed having the filter() method on my arrays/lists and the ability to do that simply. I also wish we had a choice() method for arrays like Python and other languages do to allow us to get a rnadom element from a list. But we have other things so we can’t complain. On with the build! Let’s create a simple Flow to use this method and expose it on the Campaign page through a button.
Flow Ho Ho
I thought a Flow would be the best way of allowing me to preview the outcome when testing, allow me to send the SMS messages simply, and also add the code to a button on a page. Overall the Flow is pretty simple, we call our Invocable Action passing in the Campaign Id using the {!recordId} variable, then we display that on a screen for review, and finally loop over the campaign members and send the messages before updating them. The flow overfall looks like this:

We have a coupole of interesting items to note in the flow. Firstly, I am using a repeater with a toggle to show and hide the contents, which outputs the assignments. This is handy for debuggging and for checking before sending.

Next, we have a text template that uses the Campaign Member variable in the loop to create our SMS body dynamically. Note to show as plain text and remove all the HTML tags for nicer formatting on the message.

Santa’s Messaging Service - SMS
And finally, we are sending the messages using Messaging Made Easy from Groundwork Apps which is the ISV I run. It is a very quick and easy way of adding messaging via SMS, WhatsApp, and Viber. Here I am using SMS for the wonderful acronym pun, but setting up the same for WhatsApp and Viber is also very simple. As you can see in the configuration below, I simply let it know what phone number to send to, what message to send, and optionally, as done here, which Lead to attach the Message record to for auditing, and we are off like Rudolph on Christmas Eve!

Seeing if our solution sleighs
After adding the Flow as a button to our Campaign page, we are ready to test! Here you can see us pressing the button, previewing the assignments and then the records being updated:

And I ensured for the test all the numbers were mine to check them coming through

Gift wrapping it up
Hopefully you’ve found this a fun little diversion into a silly Christmas solution I put together to help me out with a problem. If you want to learn more about Messaging Made Easy then please visit either the Groundwork Apps website or our listing on the AppExchange. Apologies for all the puns and Merry Christmas everyone!