Email Module

Introducing the Email Module

The email job is a powerful DMSContainer built-in job provided in “the box”. The job email allows to send textual and html email with attachments and related attachments. Moreover, the email job allows to send email with a delay, that’s it, you can plan a send and the job will take care of your message. When you create a new message the email is not send in that moment but is actually sent at the next scheduled run of the job. Usually the email job is scheduled to run each minute or each 5 minutes. A delayed email is send “not before” a specified date, in others word, at the next run after that time stamp. In the next paragraph are listed all the methods available in the email job RPC interface.

EMail Module as Events Data Source

Since DMSContainer 4.0.x the Email Module act as DataSource for the EventStreams Module. These means that while its normal activities run, the Email Module emits events in particular queues to notify these activities. In particular any sent email message create a new event in a queue named “queues.jobs..email.sent” and for each sending error creates a new event in a queue named “queues.jobsemail.error”. By default the email module is configured with the name “emails”, so, by default, you will get these messages in the queues “queues.jobs.email.sent” and “queues.jobs.email.sent”. This particular integration open endless possibilities, for instance you can choose to start some process only if an email cannot be sent, o just plug further elaborations when an email is sent and so on. Really, sky is the limit.

Authentication

The email job uses JWT to do users authentication and authorization. The login method inherited from the Auth Module can be called even in the Email Module and returns a token valid for the next 30 minutes. Any other method but login, requires a valid token as the first parameter.

The User Sender

The Email Module sends email on behalf of a DMS user. It sends email using a specific SMTP configuration called “User’s Sender”. To be able to send emails a DMS user must be configured with a User’s Sender. Check the SetUserSender API for more info.

Return Receipt

If you send an email using a professional SMTP client there are two optional features that can be configured:

  1. Request a delivery receipt for this message.
  2. Request a read receipt for this message.

If you check “delivery receipt”, the “Return-Receipt-To” header field is added to your email. For example:

Return-Receipt-To: "bit Time Professionals" <professionals@bittime.it>

If you check “read receipt”, the “Disposition-Notification-To” header field is added to your email. For example:

Disposition-Notification-To: "bit Time Professionals" <professionals@bittime.it>

The delivery receipt (header Return-Receipt-To) is a request for the receiving mail server to send a Delivery Status Notification (a.k.a. DSN) as soon as it receives the email.

The read receipt (header Disposition-Notification-To) is a request for the receiving email client to send a DSN as soon as the person opens the email.

Both header fields can be added checking “Ask for return receipt” in the “Sender Configuration” form of each user.

However, while the Email Modules does its best to be standard compliant, as the RFC says, ““he presence of a Disposition-Notification-To header in a message is merely a request for an MDN. The recipients’ user agents are always free to silently ignore such a request. Alternatively, an explicit denial of the request for information about the disposition of the message may be sent using the “denied” disposition in an MDN.”

Reply-To

When you send an email to a subscriber and they click ‘Reply’, the reply message is typically sent to the email address listed in the From header.

A Reply-To address is identified by inserting the Reply-To header in your email. It is the email address that the reply message is sent when you want the reply to go to an email address that is different than the From address.

In the example below, ‘professionals@bittime.it’ is the Reply-To address. When a subscriber clicks ‘Reply’, the reply message is sent to ‘professionals@bittime.it’ instead of ‘marketing@bittime.it’.

From: marketing@bittime.com
To: pparker@spideycorporation.com
Reply-To: professionals@sampledomain.com

DMSContainer’ Email Module allows to set a custom Reply-To header setting the property msgreplytolist as defined in the API. Check the samples to know all the details.

Email Module APIs

The Email Module provides a number of APIs to manage messages, bulk messages (a.k.a. emailing list or newslettera) users and senders.

Login

Allows to log in into the system. It’s inherited by the Auth Module.

SetUserSender

Defines a sender for an user. If a user has a sender that user can send emails, otherwise cannot send emails.

PARAMS
token: A valid token
userid: Userid of the user to whom define the sender
obj: A json object with the following properties:
     - senderaddress: The email that will be used to send the email (e.g. peter.parker@bittime.it);
     - smtphost: The smtphost that the user will use to send emails (e.g. smtp.gmail.com);
     - smtpport: The smtpport where the smtpserver listen (e.g. 587);
     - smtpusername: The SMTP username (e.g. peter.parker);
     - smtppassword: The SMTP password (e.g. spidey2000);
     - smtpusessl: A boolean value which defines if the SMTP server needs SSL or not
     - smtpsslversion: One of the following values (as string) depending from the SMTP provider:
       SSLv2, SSLv23, SSLv3, TLSv1, TLSv1_1, TLSv1_2
       This field is ignored if smtpusessl = false

RETURNS
none

REQUIRED ROLES
admin

Example

var

  lResp: IJSONRPCResponse;
  lJSONParam: TJsonObject;
  lJSON: TJsonObject;
  lUserID: Integer;
  lNotification: IJSONRPCNotification;
begin
  lNotification := TJSONRPCNotification.Create('SetUserSender');
  lUserID := 1;
  lNotification.Params.Add(fToken);
  lNotification.Params.Add(lUserID);
  lJSON := TJsonObject.Create;
  lNotification.Params.Add(lJSON);
  lJSON.S['senderaddress'] := 'd.teti@bittime.it';
  lJSON.S['smtphost'] := 'smtp.gmail.com';
  lJSON.I['smtpport'] := 587;
  lJSON.S['smtpusername'] := 'd.teti@bittime.it';
  lJSON.S['smtppassword'] := 'your password';
  lJSON.B['smtpusessl'] := True;
  lJSON.S['smtpsslversion'] := 'TLSv1';
  lResp := fExecutor.ExecuteNotification(lNotification);
  ShowMessage('Sender set for user');
end;

RemoveUserSender

Remove the sender for an user so that it cannot send emails anymore

PARAMS
token: A valid token
userid: The userid of the user to whom remove the sender

RETURNS
none

REQUIRED ROLES
admin

Example

var
  lReq: IJSONRPCRequest;
  lResp: IJSONRPCResponse;
  lJSON: TJsonObject;
  lUserID: Integer;
begin
  lUserID := 99;

  lReq := TJSONRPCRequest.Create(1234, 'removeusersender');
  lReq.Params.Add(fToken);
  lReq.Params.Add(lUserID);
  lResp := fExecutor.ExecuteRequest(lReq);
  ShowMessage('User sender removed');
end;

SendMessage

Add a simple message (without attachments) into the messages queue

PARAMS
token: A valid token
obj: A json object with the following properties:
     - msgtolist: comma separated email address
     - msgcclist: comma separated email address
     - msgbcclist: comma separated email address
     - msgreplytolist: comma separated email address
     - msgsubject: the subject of the email
     - istest: if true the email is not actually sent, used for debug (optional, default false)
     - msgbody: the textual body of the email (optional, default empty)
     - msgbodyhtml: the html body of the email (optional, default empty)
     - msgnote: note for the others user, doesn't affect the sent email (optional, default empty)

RETURNS
messageid: how the message is identified by the system

REQUIRED ROLES
sender

Example

var
  lReq: IJSONRPCRequest;
  lResp: IJSONRPCResponse;
  lJSONParam: TJsonObject;
begin
  lReq := TJSONRPCRequest.Create(1234, 'sendmessage');
  lReq.Params.Add(Token);
  lJSONParam := TJsonObject.Create;
  try
    lJSONParam.S['msgbody'] := 'This is the message sent at ' + DateTimeToStr(Now);
    lJSONParam.S['msgbodyhtml'] := 'This is the HTML message sent at ' + DateTimeToStr(Now);
    lJSONParam.S['msgsubject'] := '[DMS EMAIL CLIENT TEST] This is the subject';
    lJSONParam.S['msgtolist'] := 'larry@google.com,sergey@google.com,timothy@apple.com';
    lJSONParam.S['msgcclist'] := ''; //optional
    lJSONParam.S['msgbcclist'] := 'larry@oracle.com'; //optional
    lJSONParam.S['msgreplytolist'] := '"Spidey" <pparker@email.com>'; //optional
    lReq.Params.Add(lJSONParam);
  except
    lJSONParam.Free;
    raise;
  end;
  lResp := fRPCExecutor.ExecuteRequest(lReq);
  ShowMessage('Message queued with messageid = ' + lResp.ResultAsJSONObject.I['messageid'].ToString);

GetMessageByID

Retrieve a message by id

PARAMS
token: A valid token
messageid: The id of the message to retrieve

RETURNS
obj: The retrieved message
Raises an exception: if the message doesn't exist

REQUIRED ROLES
If `sender` the user can retrieve only its messages
If `admin` or `monitor` the user can retrieve all the messages

Python example

import dmsproxy

proxy = dmsproxy.DMSProxy("https://localhost:443/emailrpc")
proxy.login("user_sender", "pwd1")
message = proxy.get_message_by_id(472)
print(message)

The output is shown below

{
  "msgbody": "This is a text body",
  "msgbodyhtml": "This is a <b>html</b> body",
  "msgnote": "",
  "msgfromaddress": "peter.parker79@libero.it",
  "smtphost": "smtp.libero.it",
  "smtpport": 465,
  "smtpusername": "peter.parker79@libero.it",
  "smtppassword": "<hidden>",
  "smtpusessl": True,
  "smtpsslversion": "TLSv1_1",
  "id": 472,
  "senderuserid": 1,
  "owneruserid": 1,
  "msgtolist": "d.teti@bittime.it,daniele.teti@gmail.com",
  "msgcclist": "",
  "msgbcclist": "",
  "msgsubject": "Here's the files you need",
  "status": "SENT",
  "createdat": "2019-01-09T12:07:13.853Z",
  "sentat": "2019-01-09T12:07:19.281Z",
  "sendnotbeforeof": None,
  "retrycount": 0,
  "lasterror": "",
  "istest": False
}

DeleteMessageByID

Deletes a message by id

PARAMS
token: A valid token
messageid: The id of the message to delete

RETURNS
none
Raises an exception: if the message doesn't exist

REQUIRED ROLES
If `sender` the user can retrieve delete its non `SENT` messages
If `admin` the user can delete any message in any state

Python example

import dmsproxy

proxy = dmsproxy.DMSProxy("https://localhost:443/emailrpc")
proxy.login("user_sender", "pwd1")
proxy.delete_message(472)

GetMessagesByUsername

Retrieve a message for a user

PARAMS
token: A valid token
username: The username of the messages

RETURNS
items: A list of messages
Raises an exception: if the message doesn't exist

REQUIRED ROLES
admin or monitor

Python example

import dmsproxy

proxy: dmsproxy.DMSProxy = dmsproxy.DMSProxy("https://localhost:443/emailrpc")
proxy.login("user_monitor", "pwd1")
messages = proxy.get_messages_by_username("user_sender")
print(messages)

The output is shown below

[
  {
    "msgbody": "hello body",
    "msgbodyhtml": "This is a <b>html</b> body",
    "msgnote": "",
    "msgfromaddress": "d.teti@bittime.it",
    "smtphost": "smtp.gmail.com",
    "smtpport": 587,
    "smtpusername": "d.teti@bittime.it",
    "smtppassword": "<hidden>",
    "smtpusessl": True,
    "smtpsslversion": "TLSv1",
    "id": 380,
    "senderuserid": 1,
    "owneruserid": 1,
    "msgtolist": "d.teti@bittime.it",
    "msgcclist": "",
    "msgbcclist": "",
    "msgsubject": "This is the subject #1",
    "status": "SENT",
    "createdat": "2018-01-04T16:40:33.832Z",
    "sentat": "2019-01-04T16:41:02.060Z",
    "sendnotbeforeof": "2018-01-04T16:40:32.740Z",
    "retrycount": 0,
    "lasterror": "",
    "istest": False
  },
  "...other json objects..."
]

GetMessagesByStatus

Retrieve messages by status

PARAMS
token: A valid token
statuslist: A json array containing statuses (e.g. ['TO_SEND','SENT'])

RETURNS
items: A list of messages
Raises an exception: if the statuses are not valid

REQUIRED ROLES
admin or monitor

Delphi example

var
  lReq: IJSONRPCRequest;
  lResp: IJSONRPCResponse;
  lJSONParam: TJsonArray;
begin
  lReq := TJSONRPCRequest.Create(1234, 'getmessagesbystatus');
  lReq.Params.Add(Token);
  lJSONParam := TJsonArray.Create;
  try
    lJSONParam.Add('ERROR');
    lJSONParam.Add('TO_SEND');
    // lJSONParam.Add('SENT');
    lJSONParam.Add('NOT_COMPLETED');
    lReq.Params.Add(lJSONParam);
  except
    lJSONParam.Free;
    raise;
  end;
  lResp := fRPCExecutor.ExecuteRequest(lReq);

  //mtMessages is a TFDMemTable
  mtMessages.Close;
  mtMessages.Open;
  mtMessages.LoadFromJSONArray(lResp.ResultAsJSONArray);

Python example

import dmsproxy

proxy: dmsproxy.DMSProxy = dmsproxy.DMSProxy("https://localhost:443/emailrpc")
proxy.login("user_monitor", "pwd1")
messages = proxy.get_messages_by_status(['TO_SEND','SENT'])
print(messages)

The output is shown below

[
  {
    msgbody: "This is a text body",
    msgbodyhtml: "This is a <b>html</b> body",
    msgnote: "",
    msgfromaddress: "d.teti@bittime.it",
    smtphost: "smtp.gmail.com",
    smtpport: 587,
    smtpusername: "d.teti@bittime.it",
    smtppassword: "",
    smtpusessl: True,
    smtpsslversion: "TLSv1",
    id: 380,
    senderuserid: 1,
    owneruserid: 1,
    msgtolist: "d.teti@bittime.it",
    msgcclist: "",
    msgbcclist: "",
    msgsubject: "This is the subject #1",
    status: "SENT",
    createdat: "2019-01-04T16:40:33.832Z",
    sentat: "2019-01-04T16:41:02.060Z",
    sendnotbeforeof: "2019-01-04T16:40:32.740Z",
    retrycount: 0,
    lasterror: "",
    istest: False,
  },
  "...other json objects...",
];

CreateMessage

Create message with the abilities to contains attachments, into the messages queue. The message must be completed using completemessage before to be enqueued in the message queue. If attachments are needed, issue a call to addattachmenttomessage for each attachment.

PARAMS
token: A valid token
obj: A json object with the following properties:
     - msg_to_list: comma separated email address
     - msg_cc_list: comma separated email address
     - msg_bcc_list: comma separated email address
     - msg_subject: the subject of the email
     - is_test: if true the email is not actually sent, used for debug (optional, default false)
     - msg_body: the textual body of the email (optional, default empty)
     - msg_body_html: the html body of the email (optional, default empty)
     - msg_note: note for the others user, doesn't affect the sent email (optional, default empty)
     - send_not_before_of: do not send the message before this timestamp (iso format)

RETURNS
messageid: how the message is identified by the system

REQUIRED ROLES
sender

Example

var
  lReq: IJSONRPCRequest;
  lResp: IJSONRPCResponse;
  lJSONParam: TJsonObject;
begin
  lReq := TJSONRPCRequest.Create(1234, 'createmessage');
  lReq.Params.Add(Token);
  lJSONParam := TJsonObject.Create;
  try
    lJSONParam.S['msgbody'] := 'This is the message sent at ' + DateTimeToStr(Now);
    lJSONParam.S['msgbodyhtml'] := 'This is the HTML message sent at ' + DateTimeToStr(Now);
    lJSONParam.S['msgsubject'] := '[DMS EMAIL CLIENT TEST] This is the subject';
    lJSONParam.S['msgtolist'] := 'larry@google.com,sergey@google.com,timothy@apple.com';
    lJSONParam.S['msgcclist'] := '';
    lJSONParam.S['msgbcclist'] := 'larry@oracle.com';
    lReq.Params.Add(lJSONParam);
  except
    lJSONParam.Free;
    raise;
  end;
  lResp := fRPCExecutor.ExecuteRequest(lReq);
  ShowMessage('Message queued with messageid = ' + lResp.ResultAsJSONObject.I['messageid'].ToString);

CompleteMessage

Inform the system that a message created with createmessage is ready to be sent. If attachments are needed, issue a call to addattachmenttomessage for each attachment before the call to completemessage.

PARAMS
token: A valid token
messageid: The messageid of the message that must be completed

RETURNS
none

REQUIRED ROLES
sender

Example

See example about addattachmenttomessage

AddAttachmentToMessage

addattachmenttomessage must be called after createmessage and before completemessage for each attachment needed.

PARAMS
token: A valid token
messageid: The messageid to whom the attachment must be added
isrelated: A boolean value. If True means that the attachment is used into the HTML body of the message obj: A json object with the following properties:
     - filename: the name of the file
     - filedata: the contents of the datafile encoded as base64

RETURNS
none

REQUIRED ROLES
sender

Delphi example

var
  lReq: IJSONRPCRequest;
  lResp: IJSONRPCResponse;
  lJSONParam: TJsonObject;
  lMsgID: Integer;
  lAttachment: String;
  lAttachments: TArray<String>;
begin
  // create the message
  lReq := TJSONRPCRequest.Create(1234, 'createmessage');
  lReq.Params.Add(Token);
  lJSONParam := TJsonObject.Create;
  try
    lJSONParam.S['msgbody'] := 'This message has some attachments';
    lJSONParam.S['msgbodyhtml'] := 'This message <span style="color: red">has</span> some attachments';
    lJSONParam.S['msgsubject'] := '[DMS EMAIL CLIENT TEST] This is the subject';
    lJSONParam.S['msgtolist'] := 'bill@microsoft.com';
    lJSONParam.S['msgcclist'] := 'larry@oracle.com,johndoe@gmail.com';
  except
    lJSONParam.Free;
    raise;
  end;
  lReq.Params.Add(lJSONParam);
  lResp := fRPCExecutor.ExecuteRequest(lReq);
  lMsgID := lResp.ResultAsJSONObject.I['messageid'];

  // add all the attachments
  lAttachments := ['file1.png', 'file2.pdf'];
  for lAttachment in lAttachments do
  begin
    lReq := TJSONRPCRequest.Create(1234, 'addattachmenttomessage');
    lReq.Params.Add(Token);
    lReq.Params.Add(lMsgID);
    lReq.Params.Add(false); // is not related
    lJSONParam := TJsonObject.Create;
    try
      lJSONParam.S['filename'] := lAttachment;
      lJSONParam.S['filedata'] := TNetEncoding.Base64.EncodeBytesToString(TFile.ReadAllBytes(lAttachment));
      lReq.Params.Add(lJSONParam);
    except
      lJSONParam.Free;
      raise;
    end;
    fRPCExecutor.ExecuteRequest(lReq);
  end;

  // complete the message
  lReq := TJSONRPCRequest.Create(1234, 'completemessage');
  lReq.Params.Add(Token);
  lReq.Params.Add(lResp.ResultAsJSONObject.I['messageid']);
  fRPCExecutor.ExecuteRequest(lReq);
end;

Python example

This example uses the built-in python proxy for the DMSContainer email job available in the Python module dmsproxy.py.

import dmsproxy
from pathlib import Path

proxy = dmsproxy.DMSProxy("https://localhost:443/emailrpc")
proxy.login("user_sender", "pwd1")
msg = {
    "msgtolist": "larry@oracle.com",
    "msgbody": "This is a text body",
    "msgbodyhtml": "This is a <b>html</b> body",
    "msgsubject": "Here's the files you need",
    "is_test": False
}

#create the message

message_id = proxy.create_message(**msg)

# add first attachment

att1 = Path(__file__).parent.joinpath('testdata').joinpath('file1.png')
proxy.add_attachment(message_id, str(att1), False)

# add second attachment

att2 = Path(__file__).parent.joinpath('testdata').joinpath('file2.pdf')
proxy.add_attachment(message_id, str(att2), False)

# complete the message

proxy.complete_message(message_id)
print("Message correctly enqueued with messageid: " + str(message_id))

CompleteMessage

Inform the system that a message created with createmessage is ready to be sent. If attachments are needed, issue a call to addattachmenttomessage for each attachment before the call to completemessage.

PARAMS
token: A valid token
messageid: The messageid of the message that must be completed

RETURNS
none

REQUIRED ROLES
sender

Example

See example about addattachmenttomessage

BulkSendMessages

Send a bulk message to a list of email addresses. The message can have also a list of attachments. The body and the attachments can be template driven. If the message contains a template (body or attachment) reportdata must contains the data to generate them.

PARAMS

token: A valid token

aMetaMessage: A json object with the following properties:
     - msgsubject: the subject of the email (can contains template tags)
     - istest: if true the email is not actually sent, used for debug (optional, default false)
     - msgbody: the textual body of the email (can contains template tags) (optional, default empty)
     - msgbodyhtml: the html body of the email (can contains template tags) (optional, default empty)
     - msgnote: note for the others user, doesn't affect the sent email (optional, default empty)
     - attachments: json array of "attachment" json object. Each attachment has the following properties
         - filename: the filename shown to the user (can contains template tags)
         - filedata: the contents of the file. If `is_template` is `true` the filedata **must** be a docx file
         - is_template: boolean value. If true the `filedata` must be a docx file and the attached file will be a PDF.

aBulkMessagesData: A json object with the following properties:
     - meta: ajsonobject containing data shared for all the templates. No specific format is enforced.
     - recipients: a json array which contains the recipents data used for each message. Will be generated one message for each `recipient`.  Each item contains the following properties:
         - msgtolist: comma separated email address
         - msgcclist: comma separated email address
         - msgbcclist: comma separated email address
         - refid: the reference id used to identify the return; usually is the primary key of the database table which should track the sent messages.
     - items: a json array which contains the data used for each message. items can be empty or zero length, but if it exists then must be of the same size of the `recipents` array. Each item can contains any property and those data will be used for each "template" part of the message (subject, body, body_html, attachment etc so on).

RETURNS
messagesid: an array of integer containing the generated messages id.

REQUIRED ROLES
sender

Example

procedure TMainForm.btnSendBulkMessagesClick(Sender: TObject);
var
  lMetaMessage: TJSONObject;
  lMessageData: TJSONObject;
  lRecipient: TJSONObject;
  lItem: TJSONObject;
  lJObj: TJSONObject;
begin
  /// Let's prepare the metadata of the email message
  /// ////////////////////////////////////////////////////////
  lMetaMessage := TJSONObject.Create;
  try
    lMetaMessage.S['msgsubject'] :=
      '[DMSContainer Email TEST] {{meta.title}} - This email is for {{data.nome}} {{data.cognome}}';
    lMetaMessage.S['msgbody'] := '';
    lMetaMessage.S['msgbodyhtml'] := TFile.ReadAllText('template_invito_email_10.html', TEncoding.UTF8);
    lMetaMessage.S['msgnote'] := '';
    lMetaMessage.B['istest'] := false;
    FillWithAttachments(lMetaMessage);
  except
    lMetaMessage.Free;
    raise;
  end;

  /// Let's prepare the actual recipients data
  /// ////////////////////////////////////////////////////////
  lMessageData := TJSONObject.Create;
  try
    /// META
    lMessageData.O['meta'].S['title'] := 'This is the title';

    /// RECIPIENTS
    lRecipient := lMessageData.A['recipients'].AddObject;
    lRecipient.S['msgtolist'] := 'theboss@mydomain.it';
    lRecipient.S['msgcclist'] := 'thebossassistantoffice@mydomain.it';
    lRecipient.S['msgbcclist'] := 'mysecretemail@mydomain.it';
    lRecipient.I['refid'] := 1;

    lRecipient := lMessageData.A['recipients'].AddObject;
    lRecipient.S['msgtolist'] := 'thecustomer@mydomain.it';
    lRecipient.I['refid'] := 2;

    lRecipient := lMessageData.A['recipients'].AddObject;
    lRecipient.S['msgtolist'] := 'thesupplier@mydomain.it';
    lRecipient.I['refid'] := 3;

    /// ITEMS
    lItem := lMessageData.A['items'].AddObject;
    lItem.S['nome'] := 'Daniele';
    lItem.S['cognome'] := 'Teti';
    lItem.S['cerimonia'] := '35° Anniversario Ufficio Affari Vari ed Eventuali';
    lItem.S['luogo_cerimonia'] := 'P.zza 1° Maggio';
    lItem.S['data_cerimonia'] := '1° maggio 2019';

    lItem := lMessageData.A['items'].AddObject;
    lItem.S['nome'] := 'Peter';
    lItem.S['cognome'] := 'Parker';
    lItem.S['cerimonia'] := '35° Anniversario Ufficio Affari Vari ed Eventuali';
    lItem.S['luogo_cerimonia'] := 'P.zza 1° Maggio';
    lItem.S['data_cerimonia'] := '1° maggio 2019';

    lItem := lMessageData.A['items'].AddObject;
    lItem.S['nome'] := 'Sue';
    lItem.S['cognome'] := 'Storm';
    lItem.S['cerimonia'] := '35° Anniversario Ufficio Affari Vari ed Eventuali';
    lItem.S['luogo_cerimonia'] := 'P.zza 1° Maggio';
    lItem.S['data_cerimonia'] := '1° maggio 2019';
  except
    lMessageData.Free;
    raise;
  end;

  lJObj := fEmailRPCProxy.BulkSendMessages(Token, lMetaMessage, lMessageData);
  try
    ShowMessage('Messages queued: ' + lJObj.A['messagesid'].ToJSON(false));
  finally
    lJObj.Free;
  end;
end;

procedure TMainForm.FillWithAttachments(aMetaMessage: TJSONObject);
var
  lJSON: TJSONObject;
begin
  lJSON := aMetaMessage.A['attachments'].AddObject();

  // Parametric attachment template as docx
  lJSON.S['filename'] := 'Invito Mr {{data.cognome}} {{data.nome}}.pdf';
  lJSON.S['filedata'] := FileToBase64String('template_invito_email_10.docx');
  lJSON.B['istemplate'] := true;

  // Non parametric attachment
  lJSON := aMetaMessage.A['attachments'].AddObject();
  lJSON.S['filename'] := 'Dressing Code for Mr {{data.cognome}} {{data.nome}}.pdf';
  lJSON.S['filedata'] := FileToBase64String('non_template_attachment.pdf');
  lJSON.B['istemplate'] := false;
end;

See also