Skip to content

Technical

TramlClient Class

The TramlClient class is the main interface for the library. The constructor receives the following parameters:

Parameter Name Type Default Value Description
app LimeApplication None An instance of the Lime CRM application
send_context SendContext None An instance of the SendContext class. If left empty it will create a send context from the config application_config
validator Validator Validator And instance of the Validator class. If left empty it will use the default regexp for email and mobile number validation
runtime_config TramlRuntimeConfig None The runtime config. If left empty it will try to load the runtime config from lime_data using the LimeApplication or fallback to a default config
application_config TramlApplicationConfig None The application config containing the test settings, API url and key to Lime Marketing. If left empty it will try to load the application config from lime_config using the LimeApplication

It is possible to use TRAML without a Lime CRM Application if a custom SendContext is supplied together with a config dict with the API info.

Basic Usage

Create a TramlClient instance by passing the LimeApplication. Configs will be resolved automatically

import limepkg_transactional_message_library.traml as traml

tc = traml.TramlClient(application)

# do stuff with traml client

Advanced Usage

You can use TramlClient without a LimeApplication and instead pass runtime and/or application config. This is done by passing TramlApplicationConfig config to TramlClient and optionally a custom SendContext.

  • By passing a custom SendContext the override and dry run settings will be picked up from the passed SendContext.
  • By passing a custom TramlApplicationConfig its possible to override all the settings otherwise picked up from the application configuration. A default send context will also be created from those settings if a SendContext wasn't passed.

Methods Available in TramlClient:

send_transactionmail_by_template

Send a single transactional email based on a template in Lime Marketing. The Email will be automatically validated to make sure that the recipient and from addresses are valid, that there is a subject and that any attachments are valid.

Parameter Name Type Description
message Email An Email model
template_id int The id of the mail template
Returns Description
TransactionMailModel A model containing the data of the sent Email message if successful

send_transactionmail

Send a single transactional email together with its HTML content. The Email will be automatically validated to make sure that the recipient and from addresses are valid, that there is a subject and that any attachments are valid.

Parameter Name Type Description
message Email An Email model
html_content string The HTML content as a string
text_content string The text content as a string
Returns Description
TransactionMailModel A model containing the data of the sent Email message if successful

send_sms

Send a single transactional SMS. The SMS will be automatically validated to make sure that the recipient and from numbers are valid and that there is a text content

Parameter Name Type Description
message Sms An Sms model
text str The content of the SMS message
Returns Description
TransactionSmsModel A model containing the data of the sent SMS message if successful

get_mail_template_id_by_name

Get the id of an email template based on its name in Lime Marketing

Parameter Name Type Description
name string The name of the email template
Returns Description
int The id of the email template if successful

Generate a link to let the recipient add a booking to their calendar. Don't forget to add the link to the email content

Parameter Name Type Description
calendar_link CalendarLink A CalendarLink model
Returns Description
string The generated url to download the ical file

Email Class

The Email class holds all the settings for an email to be sent except the content. The content is passed to TramlClient as raw html/text or a template when sending an email. The properties for an Email are:

Parameter Name Type Required Default Value Description
recipient_name string Yes The name of the recipient
recipient_email string Yes The email address of the recipient
from_name string Yes The from name of the email
from_email string Yes The from email address of the email
subject string Yes The subject
scheduled_send_date datetime No None Date and time if the message is scheduled. When omitted or set to None the message will be sent ASAP
reply_to_email string No None Set a reply-to email address to the message.
sender_name string No None The name of the sender. Only for use special use cases. Read more here
sender_email string No None The email address of the sender. Only for use special use cases. Read more here
attachments List[Attachment] No [] A list of attachments
merge_codes dict No {} A dictionary containing merge codes
external_id string No Bind the email message to an external id (a Help desk id for example).
track_openings bool No False Should message openings be tracked.
track_link_clicks bool No False Should message link clicks be tracked.
exclude_totaloptouts bool No False Don't send if email is present on the total opt-out list
exclude_publicationoptouts bool No False Don't send if email is present on any publication opt-out list
exclude_previousbounce bool No True Don't send if the email has bounced previously
include_email_data_in_webhook_payload bool No False Include the sent email data in the webhook payload
headers dict No {} A dictionary for custom email MIME headers to be set when the email is sent
options dict No {} A dictionary for additional payload parameters to Bedrock

Sender Name and Sender Email

If the sender_name and sender_email parameters are used in the Email model they will be shown as "on behalf of" the from headers:

The recipient will receive an email like this:

image
Example of how on behalf of looks in Outlook email client

Read more about the difference between the From and Sender headers here.

Warning

Using sender_name and sender_email is an option which you shouldn't have to use very often since it can cause deliverability problems if not used correctly. Make sure its a valid use case before using these settings. An alternative could be to set a more descriptive from_name Sven Svensson via Super Solution.

Headers

With the headers parameter it's possible to pass custom headers that will be used when the email is sent from Lime Marketing. Typical use cases can be to set priority, autoreply and in reply to and other email meta data that isnt possible to do via the Lime Marketing Bedrock API

Header keys and values must be of type string or None

The headers can be set directly on the Email class or by using the helper functions

Warning

Use headers with caution since it may have effect on the deliverability of the email

update_headers

Updates the current headers in a safe way - will overwrite existing values.

Parameter Name Type Description
headers dict New headers to update current headers with

set_autoreply_headers

Sets the headers used to mark an email as an auto reply. This is to tell receiving part to not respond with an auto reply or out of office message.

Parameter Name Type Description
headers dict New headers to update current headers with

Headers that are set. There is no "standard" way to this but the following headers are the safest.

Name Value
Auto-Submitted auto-replied
X-Auto-Response-Suppress All
Precedence auto_reply

set_in_reply_to_headers

Sets the headers to mark the email as a part of a conversation/thread. Pass the message id of which this email is a reply to

Parameter Name Type Description
message_id str The message id of the email this is a reply to. Usually formatted like <[email protected]>

Headers that are set. There is no "standard" way to this but the following headers are the safest.

Name Value
In-Reply-To Value of message_id
Reference Value of message_id

set_priority_headers

Sets the different priority headers based on importance where 1 = High, 3 = Normal and 5 = Low. Normal is the same as not setting a priority.

Parameter Name Type Description
priority EmailPriority High, Normal, Low

Headers that are set. There is no "standard" way to this but the following headers are the safest.

Name Value
Importance High|Normal|Low
X-Priority 1|3|5
X-MSMail-Priority High|Normal|Low

Saving a copy of the sent email message in Lime

By setting the include_email_data_in_webhook_payload parameter to True when sending an email you will be able to save the email content in Lime CRM. When the parameter is activated an exact copy of the email that was sent to the recipient will be included in the payload of the transactionmail.sent webhook. The email content data will be accessible in the TransactionMailContent property of the webhook payload in the form of a byte array. You can save this file as a document in Lime CRM by using the TramlEmailMessage class. Read more about its usage here.

It is important to note that opening the file or clicking any links in the file may result in link-click and opening events to be triggered, just as if the original recipient has opened or clicked the email message.

Sms Class

The Sms class holds all the settings for an sms to be sent except the content. The content is passed to TramlClient as raw text when sending an sms. The properties for an Sms are:

Parameter Name Type Required Default Value Description
from_number string Yes The alphanumeric sender (allowed chars are a-z0-9åäöæøü_- and whitespace and must be 2-11 chars long)
destination_number string Yes The mobile number of the recipient must be country code prefixed
scheduled_send_date datetime No None Date and time if the message is scheduled. When omitted or set to None the message will be sent ASAP
max_parts int No 10 When the content is longer than 160 chars or special unicode char are used multiple SMS messages needs to be sent to transfer the whole content. With this setting you limit the number of sms messages used but it may result in truncating the content
delivery_timeout_hours int No 24 Timeout in hours for how long the sms should try to be delivered to the recipient
merge_codes dict No {} A dictionary containing merge codes
options dict No {} A dictionary for additional payload parameters to Bedrock

The CalendarLink class holds the settings to be able to generate a calendar link that can be inserted to your Email content. The start_date and end_date parameters supports timezone offset. Example given here.

Parameter Name Type Required Default Value Description
start_date datetime Yes The start time of the calendar booking
end_date datetime Yes The end time of the calendar booking
summary str No None The title of the calendar booking
description str No None A short description to insert into the body text of the calendar booking
location str No None The location of the calendar booking
reminder_before_event int No 0 Specify how many minutes before the event the recipient should recieve a reminder
url str No None The url of the event (a Teams meeting link for example)

TramlEmailMessage class

The TramlEmailMessage class can be used to parse and modify the email data from the TransactionMailContent property of the transactionmail.sent webhook payload.

Parameter Name Type Required Default Value Description
data str or bytes Yes The email data

Properties

html: Access the email html content as a BeautifulSoup object. Remember to use the property setter to update the content if you modify it yourself. Raises TramlError if the message doesn't have any valid html content.

raw_string: Access the email data as a raw unformatted string.

defects: Access possible parsing errors from the email data.

content_type: Get the content type of the message from the message headers.

model_as_formatted_string

Returns the email data as a formatted string. If you want to save the message to disk you should use this method.

create_lime_file

Returns a lime_file.File with the message data and the message's subject as the filename. The file will have an .eml extension

remove_opening_tracking_from_email

Modifies the email content to remove the opening tracking. Use this before saving the data if you do not want to trigger an open event when viewing the message in Lime.

remove_linkclick_tracking_from_email

Modifies the email content to remove the linkclick tracking. Use this before saving the data if you do not want to trigger a linkclick event when viewing the message in Lime.

Example usage:

# transactionmail.sent webhook endpoint
from limepkg_transactional_message_library.traml import TramlEmailMessage

def create_document_from_transactionmail_sent_payload(payload):
    byte_data = payload["TransactionMailContent"]
    traml_model = TramlEmailMessage(data=byte_data)
    # remove the opening tracking before saving the file
    # to not trigger a false opening event when viewing the message
    traml_model = traml_model.remove_opening_tracking_from_email()
    file = traml_model.create_lime_file()
    document: LimeObject = self.application.limetypes.document()
    document.properties.type.set_by_key("email")
    repo: FileStorage = document.properties.document.get_file_repo()
    s_file = repo.save(file)
    document.properties.document.attach(s_file)
    return document

Utils Module

The utils module contain helpers methods to help out with reoccurring tasks.

create_attachment_from_document()

Creates an TRAML attachment by getting the lime_file from the document Lime Type object

Parameter Name Type Description
document lime_type.LimeType An lime document object with a document lime_file property
Returns Description
Attachment A prepared TRAML attachment containing the lime_file data, file name and mime type

create_attachment_from_lime_file()

Creates an TRAML attachment from a lime_file.File

Parameter Name Type Description
lime_file lime_file.File An lime_file object
Returns Description
Attachment A prepared TRAML attachment containing the lime_file data, file name and mime type

Testing

When using TRAML it's likely that you have tests that test some logic which will trigger a TRAML email or sms to be sent. Of course, you don't want them to be sent for real and also not cause errors due to lack of configuration. This example shows how you can mock the Bedrock class sending emails or sms to Lime Marketing and assert that the correct emails and sms should have been sent.

import limepkg_transactional_message_library.traml as traml
import pytest
from unittest.mock import MagicMock
import datetime
import random


@pytest.fixture
def mock_traml_cfg(monkeypatch):
    app_cfg = traml.models.TramlApplicationConfig(
        api_key="00000000-0000-0000-0000-000000001337",
        api_url="https://example.com/bedrock/api/",
    )

    attr_name = "limepkg_transactional_message_library.config.traml_config.get_traml_application_config"
    monkeypatch.setattr(attr_name, MagicMock(return_value=app_cfg))

    runtime_cfg = traml.models.TramlRuntimeConfig()
    attr_name = "limepkg_transactional_message_library.config.traml_config.get_traml_runtime_config"
    monkeypatch.setattr(attr_name, MagicMock(return_value=runtime_cfg))

@pytest.fixture
def mock_bedrock(monkeypatch):
    mock_bedrock = MagicMock()
    class_name = "limepkg_transactional_message_library.traml.bedrock.Bedrock"
    monkeypatch.setattr(class_name, MagicMock(return_value=mock_bedrock))

    def email_to_response(*args, **kwargs):
        email: traml.models.Email = kwargs["email"]

        def _get_json():
            return {
                "TransactionMailId": random.randint(1, 1000),
                "RecipientName": email.recipient_name,
                "RecipientEmail": email.recipient_email,
                "FromName": email.from_name,
                "FromEmail": email.from_email,
                "SenderName": email.sender_name,
                "SenderEmail": email.sender_email,
                "ReplyTo": email.reply_to_email,
                "ExternalId": email.external_id,
                "Subject": email.subject,
                "TrackOpenings": email.track_openings,
                "TrackLinkClicks": email.track_link_clicks,
                "LinkBaseUrl": "",
                "ExcludeTotalOptouts": email.exclude_totaloptouts,
                "ExcludePublicationOptouts": email.exclude_publicationoptouts,
                "ExcludePreviousBounce": email.exclude_previousbounce,
                "IncludeEmailDataInWebhookPayload": email.include_email_data_in_webhook_payload,
                "IsInternalMail": False,
                "CreationDate": datetime.datetime.now(),
            }

        mocked_return_value = MagicMock()
        mocked_return_value.status_code = 200
        mocked_return_value.json = _get_json
        mocked_return_value.ok = True
        return mocked_return_value

    def sms_to_response(*args, **kwargs):
        sms: traml.models.Sms = kwargs["sms"]
        text: str = kwargs["text"]

        def _get_json():
            return {
                "TransactionSmsId": random.randint(1, 1000),
                "DestinationNumber": sms.destination_number,
                "FromNumber": sms.from_number,
                "MaxParts": sms.max_parts,
                "ScheduledSendTime": sms.scheduled_send_date,
                "Text": text,
                "DeliveryTimeoutHours": sms.delivery_timeout_hours,
                "CreationDate": datetime.datetime.now(),
            }

        mocked_return_value = MagicMock()
        mocked_return_value.status_code = 200
        mocked_return_value.json = _get_json
        mocked_return_value.ok = True
        return mocked_return_value

    def get_template(*args, **kwargs):
        name: str = kwargs["name"]

        def _get_json():
            return {"Name": name, "Id": random.randint(1, 1000)}

        mocked_return_value = MagicMock()
        mocked_return_value.status_code = 200
        mocked_return_value.json = _get_json
        mocked_return_value.ok = True
        return mocked_return_value

    mock_bedrock.send_transactional_mail.side_effect = email_to_response
    mock_bedrock.send_transactional_mail_by_template_id.side_effect = (
        email_to_response
    )
    mock_bedrock.send_transactional_sms.side_effect = sms_to_response
    mock_bedrock.get_mailtemplate_by_name.side_effect = get_template

    return mock_bedrock


def test_mail_sent(limeapp, mock_bedrock, mock_traml_cfg):
    # do stuff that trigger traml email/sms being sent
    # mymodule.notify_gillyweed_thieves()

    # this is what I expect should have been sent
    expected_email = traml.models.Email(
        from_name="Sirius Black",
        from_email="[email protected]",
        subject="Regarding my stolen Gillyweed",
        recipient_name="Harry Potter",
        recipient_email="[email protected]",
    )

    expected_sms = traml.models.Sms(
        from_number="Hogwarts", destination_number="+46704001337"
    )

    tc = traml.TramlClient(limeapp)

    template = tc.get_mail_template_by_name("Important information")

    sent_mail_template = tc.send_transactionmail_by_template(
        message=expected_email, template_id=template.id
    )

    sent_mail_content = tc.send_transactionmail(
        message=expected_email,
        html_content="html content",
        text_content="text content",
    )

    sent_sms = tc.send_sms(message=expected_sms, text="Return the gillyweed now!")

    assert sent_mail_template.recipient_email == expected_email.recipient_email
    assert sent_mail_content.recipient_email == expected_email.recipient_email
    assert sent_sms.destination_number == expected_sms.destination_number

    # assert bedrock sent email with the correct parameters
    mock_bedrock.send_transactional_mail_by_template_id.assert_any_call(
        email=expected_email, template_id=template.id
    )

    # assert bedrock sent email with the correct parameters
    mock_bedrock.send_transactional_mail.assert_any_call(
        email=expected_email,
        html_content="html content",
        text_content="text content",
    )

    # assert bedrock sent sms with the correct parameters
    mock_bedrock.send_transactional_sms.assert_any_call(
        sms=expected_sms, text="Return the gillyweed now!"
    )

    # use assert_called_with or assert_called_once_with to assert that the LAST 
    # or ONLY request to bedrock was that one

TransactionMail Webhooks

You can see a list of all available Transactionmail webhooks here: https://app.bwz.se/bedrock/example/webhook#events

Back to top