diff --git a/go.mod b/go.mod
index de9ec15f7330b73b41780fd745bd326c43e24ba4..0b04d83613e282d1270a15687afc1cbd1e2855e3 100644
--- a/go.mod
+++ b/go.mod
@@ -21,12 +21,14 @@ require (
 	github.com/aws/smithy-go v1.20.2
 	github.com/dimuska139/go-email-normalizer/v2 v2.0.0
 	github.com/dlsniper/debugger v0.6.0
+	github.com/dongri/phonenumber v0.1.6
 	github.com/go-pg/pg/v10 v10.10.6
 	github.com/go-redis/redis/v8 v8.11.4
 	github.com/go-redis/redis_rate/v9 v9.1.2
 	github.com/go-resty/resty/v2 v2.7.0
 	github.com/golang-jwt/jwt/v4 v4.4.3
 	github.com/google/uuid v1.6.0
+	github.com/infobip/infobip-api-go-client/v3 v3.1.1
 	github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
 	github.com/jinzhu/now v1.1.5
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
@@ -69,7 +71,6 @@ require (
 	github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
-	github.com/dongri/phonenumber v0.1.6 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/go-errors/errors v1.4.1 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
@@ -119,5 +120,6 @@ require (
 	google.golang.org/grpc v1.69.2 // indirect
 	google.golang.org/protobuf v1.36.2 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
+	gopkg.in/validator.v2 v2.0.1 // indirect
 	mellium.im/sasl v0.2.1 // indirect
 )
diff --git a/go.sum b/go.sum
index fdef8661b544a331c5d515bfb3a941436fa3148e..c01dbd13c5a3d95034de4df5518b4e45bdd32085 100644
--- a/go.sum
+++ b/go.sum
@@ -184,6 +184,8 @@ github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible h1:zaX5fYT98jX5j4
 github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
 github.com/inconshreveable/log15/v3 v3.0.0-testing.5 h1:h4e0f3kjgg+RJBlKOabrohjHe47D3bbAB9BgMrc3DYA=
 github.com/inconshreveable/log15/v3 v3.0.0-testing.5/go.mod h1:3GQg1SVrLoWGfRv/kAZMsdyU5cp8eFc1P3cw+Wwku94=
+github.com/infobip/infobip-api-go-client/v3 v3.1.1 h1:hgkvZiwgLzmk9BGumElRRCxDQNhv37DuIzrGraXTXlI=
+github.com/infobip/infobip-api-go-client/v3 v3.1.1/go.mod h1:DuvEYDOank/s4FO4mQtmbOiZCTQFFr/vAWW41kZ5Wxc=
 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -482,6 +484,8 @@ gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
 gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
+gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/infobip_utils/infobip_utils.go b/infobip_utils/infobip_utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..567c6b1cc2e180aadc6275ad4472fa9731d6f955
--- /dev/null
+++ b/infobip_utils/infobip_utils.go
@@ -0,0 +1,179 @@
+package infobip_utils
+
+import (
+	"github.com/infobip/infobip-api-go-client/v3/pkg/infobip/models/messagesapi"
+)
+
+type InfobipFailover string
+
+const (
+	FailoverTypeRCS InfobipFailover = "RCS"
+	FailoverTypeSMS InfobipFailover = "SMS"
+)
+
+type MessageDetails struct {
+	From                   string
+	To                     string
+	MessageText            string
+	CallbackData           string
+	CallbackURL            string
+	ValidityPeriodAmount   int32
+	ValidityPeriodTimeUnit string
+	Variables              map[string]any
+	Failovers              []InfobipFailover
+}
+
+// ------------------------------------------------------------------------------------------------
+// Message builder functions
+// ------------------------------------------------------------------------------------------------
+
+func getDestinations(messageDetails MessageDetails) []messagesapi.MessageDestination {
+	whatsAppChannelDestination := messagesapi.NewChannelDestination(messagesapi.OUTBOUNDMESSAGECHANNEL_WHATSAPP, messageDetails.To)
+	rcsChannelDestination := messagesapi.NewChannelDestination(messagesapi.OUTBOUNDMESSAGECHANNEL_RCS, messageDetails.To)
+	smsChannelDestination := messagesapi.NewChannelDestination(messagesapi.OUTBOUNDMESSAGECHANNEL_SMS, messageDetails.To)
+
+	destinations := messagesapi.NewChannelsDestination([]messagesapi.ChannelDestination{*whatsAppChannelDestination, *rcsChannelDestination, *smsChannelDestination})
+	return []messagesapi.MessageDestination{{ChannelsDestination: destinations}}
+}
+
+func getMessageContent(messageDetails MessageDetails) messagesapi.MessageContent {
+	content := messagesapi.NewMessageContent(messagesapi.MessageBody{
+		MessageTextBody: messagesapi.NewMessageTextBody(messageDetails.MessageText),
+	})
+
+	return *content
+}
+
+func getTemplate(templateName string) messagesapi.Template {
+	template := messagesapi.NewTemplate(templateName)
+	template.SetLanguage("en_GB")
+	return *template
+}
+
+func getDefaultValidityPeriod(messageDetails MessageDetails) messagesapi.ValidityPeriod {
+	validityPeriod := messagesapi.NewValidityPeriod(messageDetails.ValidityPeriodAmount)
+	validityPeriod.SetTimeUnit(messagesapi.ValidityPeriodTimeUnit(messageDetails.ValidityPeriodTimeUnit))
+
+	return *validityPeriod
+}
+
+func getMessageOptionsForValidityPeriod(validityPeriod messagesapi.ValidityPeriod) messagesapi.MessageOptions {
+	messageOptions := messagesapi.NewMessageOptions()
+	messageOptions.SetValidityPeriod(validityPeriod)
+
+	return *messageOptions
+}
+
+func getWebhookForCallbacks(messageDetails MessageDetails) *messagesapi.OttWebhooks {
+	deliveryReporting := messagesapi.NewMessageDeliveryReporting()
+	deliveryReporting.SetUrl(messageDetails.CallbackURL)
+
+	webhooks := messagesapi.NewOttWebhooks()
+	webhooks.SetDelivery(*deliveryReporting)
+	webhooks.SetContentType("application/json")
+	webhooks.SetCallbackData(messageDetails.CallbackData)
+
+	return webhooks
+}
+
+func getRCSFailover(messageDetails MessageDetails) messagesapi.BaseFailover {
+	rcsFailover := messagesapi.NewFailover(messagesapi.OUTBOUNDMESSAGECHANNEL_RCS, messageDetails.From)
+	rcsFailover.SetValidityPeriod(getDefaultValidityPeriod(messageDetails))
+	rcsFailover.SetContent(getMessageContent(messageDetails))
+
+	return messagesapi.BaseFailover{Failover: rcsFailover}
+}
+
+func getSMSFailover(messageDetails MessageDetails) messagesapi.BaseFailover {
+	smsFailover := messagesapi.NewFailover(messagesapi.OUTBOUNDMESSAGECHANNEL_SMS, messageDetails.From)
+	smsFailover.SetValidityPeriod(getDefaultValidityPeriod(messageDetails))
+	smsFailover.SetContent(getMessageContent(messageDetails))
+
+	return messagesapi.BaseFailover{Failover: smsFailover}
+}
+
+func SetTemplateMessageValidityPeriod(message *messagesapi.TemplateMessage, messageDetails MessageDetails) {
+	validityPeriod := getDefaultValidityPeriod(messageDetails)
+	messageOptions := getMessageOptionsForValidityPeriod(validityPeriod)
+	message.SetOptions(messageOptions)
+}
+
+func SetMessageValidityPeriod(message *messagesapi.Message, messageDetails MessageDetails) {
+	validityPeriod := getDefaultValidityPeriod(messageDetails)
+	messageOptions := getMessageOptionsForValidityPeriod(validityPeriod)
+	message.SetOptions(messageOptions)
+}
+
+func SetTemplateMessageFailovers(message *messagesapi.TemplateMessage, messageDetails MessageDetails) {
+	var baseFailovers []messagesapi.BaseFailover
+	for _, failoverType := range messageDetails.Failovers {
+		switch failoverType {
+		case FailoverTypeRCS:
+			baseFailovers = append(baseFailovers, getRCSFailover(messageDetails))
+		case FailoverTypeSMS:
+			baseFailovers = append(baseFailovers, getSMSFailover(messageDetails))
+		}
+	}
+	message.SetFailover(baseFailovers)
+}
+
+func SetMessageFailovers(message *messagesapi.Message, messageDetails MessageDetails) {
+	var baseFailovers []messagesapi.BaseFailover
+	for _, failoverType := range messageDetails.Failovers {
+		switch failoverType {
+		case FailoverTypeRCS:
+			baseFailovers = append(baseFailovers, getRCSFailover(messageDetails))
+		case FailoverTypeSMS:
+			baseFailovers = append(baseFailovers, getSMSFailover(messageDetails))
+		}
+	}
+	message.SetFailover(baseFailovers)
+}
+
+func SetTemplateMessageWebhooksForCallbacks(message *messagesapi.TemplateMessage, messageDetails MessageDetails) {
+	webhooks := getWebhookForCallbacks(messageDetails)
+	message.SetWebhooks(*webhooks)
+}
+
+func SetMessageWebhooksForCallbacks(message *messagesapi.Message, messageDetails MessageDetails) {
+	webhooks := getWebhookForCallbacks(messageDetails)
+	message.SetWebhooks(*webhooks)
+}
+
+func CreateTemplateMessage(messageDetails MessageDetails, templateName string, fromAccountName string) *messagesapi.TemplateMessage {
+	destinations := getDestinations(messageDetails)
+	template := getTemplate(templateName)
+
+	content := messagesapi.NewTemplateMessageContent()
+	textBody := messagesapi.NewTemplateTextBody()
+	textBody.AdditionalProperties = messageDetails.Variables
+	contentBody := messagesapi.TemplateTextBodyAsTemplateBody(textBody)
+	content.SetBody(contentBody)
+
+	templateMessage := messagesapi.NewTemplateMessage(messagesapi.OUTBOUNDTEMPLATECHANNEL_WHATSAPP, fromAccountName, destinations, template)
+	templateMessage.SetContent(*content)
+	return templateMessage
+}
+
+func CreateWhatsAppMessage(messageDetails MessageDetails, fromAccountName string) *messagesapi.Message {
+	destinations := getDestinations(messageDetails)
+	content := getMessageContent(messageDetails)
+
+	return messagesapi.NewMessage(messagesapi.OUTBOUNDMESSAGECHANNEL_WHATSAPP, fromAccountName, destinations, content)
+}
+
+func CreateRCSMessage(messageDetails MessageDetails, fromAccountName string) *messagesapi.Message {
+	destinations := getDestinations(messageDetails)
+	content := getMessageContent(messageDetails)
+
+	return messagesapi.NewMessage(messagesapi.OUTBOUNDMESSAGECHANNEL_RCS, fromAccountName, destinations, content)
+}
+
+func CreateSMSMessage(messageDetails MessageDetails, fromAccountName string) *messagesapi.Message {
+	destinations := getDestinations(messageDetails)
+	content := getMessageContent(messageDetails)
+
+	return messagesapi.NewMessage(messagesapi.OUTBOUNDMESSAGECHANNEL_SMS, fromAccountName, destinations, content)
+}
+
+// endregion