Sending & Receiving Email
Provides your users the ability to perform email communications with a Sudo
Sending Email Messages
The Email SDK can handle sending email messages. Sending messages requires a provisioned email address as discussed in Manage Email Addresses.
The sendEmailMessage
method is used to send an email message. The first input property, emailMessageHeader
, contains header fields such as from
, to
, cc
, bcc
, replyTo
and subject
which adhere to the RFC 822 standard. Further input properties include the message body
, attachments
and inlineAttachments.
The last input property, senderEmailAddressId
, must match the id
of the email address from the from
field in the header input. A call to this method returns the id
and created
timestamp of the sent email message to allow for a client to sync data from a remote source.
Email Message End-to-end Encryption
When sending an email to in-network addresses (i.e. recipients with a provisioned email address within the Sudo Platform), the email message will be end-to-end encrypted. When sending an email to out-of-network addresses (i.e. recipients that do not have a registered email address against the Sudo Platform) the email message will not be end-to-end encrypted.
Sending an email to a mixture of in-network and out-of-network recipients in a single email message will not be end-to-end encrypted.
End-to-end encrypted email messages contain attachments which hold the encrypted RFC 822 data and the cryptographic keys used to decrypt the message. These attachments are identifed based on the properties in the following table:
A single email message can be sent to a maximum of 20 recipients at once by default. Adding recipients to the email message which exceed this limit will throw a LimitExceededError
.
// Collect the input headers, body, attachments and sender email address id
// however makes sense for your implementation.
const senderEmailAddressId: string = // ...
const emailMessageHeader: InternetMessageFormatHeader = {
from: { emailAddress: "[email protected]" },
to: [{ emailAddress: "[email protected]" }],
cc: [{ emailAddress: "[email protected]" }],
bcc: [{ emailAddress: "[email protected]" }],
replyTo: [{ emailAddress: "[email protected]" }],
subject: "Example subject line",
}
const emailAttachment: EmailAttachment = {
filename: "fooAttachment.pdf",
contentId: uuid.v4(),
mimeType: "application/pdf",
inlineAttachment: false,
data: // ... Some pdf data
}
const inlineAttachment: EmailAttachment = {
filename: "fooImage.png",
contentId: uuid.v4(),
mimeType: "image/png",
inlineAttachment: true,
data: // ... Some image data
}
try {
const input: SendEmailMessageInput = {
senderEmailAddressId: senderEmailAddressId,
emailMessageHeaders: emailMessageHeader,
body: "An example email body",
attachments: [emailAttachment],
inlineAttachments: [inlineAttachment],
}
const result = await emailClient.sendEmailMessage(input)
// `result` contains the identifier and created timestamp associated with the sent email message. You can use this to access the data of the email message.
} catch {
// Handle/notify user of error
}
Entitlements
In order to be able to send an email, the user must be entitled. To send an email the following entitlement checks are performed:
The user must be entitled to send emails by the
sudoplatform.email.emailMessageSendUserEntitled
entitlement.The email address the user is sending from must have sufficient storage capacity available as determined by the
sudoplatform.email.emailStorageMaxPerEmailAddress
entitlement.
If these entitlements' checks do not succeed, the sendEmailMessage
fails with an insufficient entitlements error.
Receiving Email Messages
Email messages can be received via subscribing to email message events or through polling on the email message retrieval APIs. See the Subscribing to Email Message Events section for information on the subscription method and see the Retrieving Email Messages section for information on the polling method.
Entitlements
While previously received emails are always able to be retrieved, in order to be able to receive new emails, the user must be entitled. To receive an email the following entitlement checks are performed:
The user must be entitled to receive emails by the
sudoplatform.email.emailMessageReceiveUserEntitled
entitlement.The email address the email is addressed to must have sufficient storage capacity available as determined by the
sudoplatform.email.emailStorageMaxPerEmailAddress
entitlement.
If these entitlements checks do not succeed, the incoming email is rejected with a reason indicating that the storage quota has been exceeded allowing the sending email service to retry.
Retrieving Email Messages
Sent and received email messages can be accessed in two ways: via its identifier (Single Email Message by Id), or via a multi access method (Multiple Email Messages).
If an email message has been deleted, it will no longer be available for access.
A fetch of single or multiple email messages can be performed remotely or locally by specifying the appropriate CachePolicy as part of the input object.
Single Email Message by Id
A single email message can be retrieved via its id
using the getEmailMessage
method. The id
is typically returned from successfully sending an email message. This method will return the record if it exists.
try {
const emailMessage = await emailClient.getEmailMessage({
id,
cachePolicy: CachePolicy.RemoteOnly,
})
// `emailMessage` contains the email message object, else `undefined` if not found.
} catch {
// Handle/notify user of errors
}
Multiple Email Messages
The ability to retrieve multiple email messages available to the user is supported. These results can be paginated.
A call to a list API will return a ListEmailMessagesResult
with a status
and depending on the status, a list of matching items
and a nextToken
to support pagination. If no results matching the input are found, the result will contain empty items. There can be three possible statuses returned:
Success
A list of all requested email messages are returned.
Partial
A list of all email messages that were successfully fetched and unencrypted are returned as well as a list of all email messages that failed to unencrypt successfully, including an error indicating the reason for the failure.
Failure
All email messages failed to be fetched or unencrypted. Contains an error indicating the reason for the failure.
An email message may fail to be unencrypted if the version of the client is not up-to-date or if the required cryptographic key is missing from the client device.
An optional dateRange
can also be supplied as part of the input to organise results. For listing email messages, the sortDate
or updatedAt
timestamps can be specified in order to perform the date range query on. The sortDate
refers to the time in which an email message is sent or received. The updatedAt
date refers to the time in which the email message was last updated. This could be due to a change in the message's seen status or the folder that it is assigned to.
Note that both timestamps cannot be specified, otherwise an InvalidArgument
error will occur.
All Email Messages
To retrieve multiple email messages that are owned by the signed in user, call the listEmailMessages
method.
const dateRange: EmailMessageDateRange = {
updatedAt: {
startDate: // startDate as a `Date` object
endDate: // endDate as a `Date` object
}
}
try {
const result = await emailClient.listEmailMessages({
dateRange,
cachePolicy: CachePolicy.RemoteOnly,
limit: 20,
nextToken,
})
if (result.status === ListOperationResultStatus.Success) {
// `result` contains the list of items matching the input.
// Page through the results if result.nextToken != undefined.
}
} catch {
// Handle/notify user of errors
}
All Email Messages for an Email Address
To retrieve multiple email messages that are assigned to a certain email address, call the listEmailAddressesForEmailAddressId
method by passing in the id
of the email address to query.
// Collect the input email address id however makes sense for your implementation.
const emailAddressId = emailAddress.id
const dateRange: EmailMessageDateRange = {
updatedAt: {
startDate: // startDate as a `Date` object
endDate: // endDate as a `Date` object
}
}
try {
const result = await emailClient.listEmailMessagesForEmailAddressId({
emailAddressId,
dateRange,
cachePolicy: CachePolicy.RemoteOnly,
limit: 20,
nextToken,
})
if (result.status === ListOperationResultStatus.Success) {
// `result` contains the list of items matching the input.
// Page through the results if result.nextToken != undefined.
}
} catch {
// Handle/notify user of errors
}
All Email Messages for an Email Folder
To retrieve multiple email messages that are assigned to a certain email folder (i.e. Trash), call the listEmailAddressesForEmailFolderId
method by passing in the id
of the email folder to query.
// Collect the input email folder id however makes sense for your implementation.
const emailFolderId = emailFolder.id
const dateRange: EmailMessageDateRange = {
updatedAt: {
startDate: // startDate as a `Date` object
endDate: // endDate as a `Date` object
}
}
try {
const result = await emailClient.listEmailMessagesForEmailFolderId({
emailFolderId,
dateRange,
cachePolicy: CachePolicy.RemoteOnly,
limit: 20,
nextToken,
})
if (result.status === ListOperationResultStatus.Success) {
// `result` contains the list of items matching the input.
// Page through the results if result.nextToken != undefined.
}
} catch {
// Handle/notify user of errors
}
Accessing RFC 822 Message Data
The Email SDK allows developers to interact with raw email data. The SDK's interfaces and documentation refer to this type of data as RFC 822 data. While there are a number of internet standards, such as RFC 2822, RFC 5322 and RFC 6854, that supersede the RFC 822 specification, the intent is that raw email data conforms to any of these specifications and is appropriate and interoperable with Sudo Platform emails.
To access the message body and headers of the email message, the RFC 822 data will need to be retrieved using the getEmailMessageRfc822Data
method. The id
of the email message is required to be passed into this method to access the data. The other methods previously supplied on this page only access the metadata of the email message.
try {
const rfc822Data = await emailClient.getEmailMessageRfc822Data({
id,
cachePolicy = CachePolicy.REMOTE_ONLY
})
// If the `id` matches an email message, `rfc822Data` will be returned, else `undefined` is returned.
if (rfc822Data) {
const emailMessageWithBody = // parse the `rfc822Data`
}
} catch {
// Handle/notify user of error
}
Retrieve Email Message with Body
To retrieve and access the contents of an email message body, attachments and headers, call the getEmailMessageWithBody
method. The id
of the email message and the id
of the associated email address is required to be passed in as input to access the message data. The other methods previously supplied on this page only access the metadata of the email message.
End-to-end encrypted email messages are decrypted using the attached cryptographic keys belonging to each recipient as specified in the table in the Sending Email Messages section and returned in the response.
// Collect the input email message and email address ids however makes sense
// for your implementation.
let emailMessageId = // ...
let emailAddressId = // ...
try {
const input: GetEmailMessageWithBodyInput = {
id: emailMessageId,
emailAddressId: emailAddressId,
}
const result = await emailClient.getEmailMessageWithBody(input)
// If the `id` matches an email message, `EmailMessageWithBody` will be returned, else `undefined` is returned.
} catch {
// Handle/notify user of error
}
Subscribing to Email Message Events
Consumers of the Email SDK may choose to subscribe to notifications of events and changes to email messages. This is done via the subscribeToEmailMessages
method. Calling the email message subscription methods in the SDK ensures that data relating to email messages will be returned as they are received on the Email Service.
It is important to use subscriptions sparingly as they are resource intensive. It is recommended to only use them when a specific view is open. For example, it would be appropriate for the application to subscribe when a view of a list of emails is in the foreground. When the user navigates from this view, the application should cancel the subscription.
Setting Up A Subscription
The subscribeToEmailMessages
function takes two parameters; a unique subscriptionId
to identify the specific subscription and an EmailMessageSubscriber
object. The EmailMessageSubscriber
will contain the functions necessary to handle changes to EmailMessage
objects as well as updates to the ConnectionState
which will be either Connected
or Disconnected
.
The EmailMessageSubscriber
object must contain the following four functions:
const emailMessageSubscriber: EmailMessageSubscriber = {
emailMessageCreated(emailMessage: EmailMessage): void {
// Implement handling for email message created behaviour
},
emailMessageUpdated(emailMessage: EmailMessage): void {
// Implement handling for email message updated behaviour
},
emailMessageDeleted(emailMessage: EmailMessage): void {
// Implement handling for email message deleted behaviour
},
connectionStatusChanged(state: ConnectionState): void {
// Implement handling for subscription connection status changed
}
}
You can then call subscribeToEmailMessages
as so:
await emailClient.subscribeToEmailMessages(subscriptionId, emailMessageSubscriber)
Cancelling A Subscription
To cancel a subscription, call the unsubscribeFromEmailMessages
method with the subscriptionId
. For example:
emailClient.unsubscribeFromEmailMessages(subscriptionId)
If the subscribe
method is called more than once with the same subscription id, subsequent invocations will replace the earlier subscriptions.
Updating Metadata of Email Messages
Updating email message metadata is supported as a batch operation. Note that this API does not allow updating the email message content and headers but rather certain attributes which make up part of the metadata.
The update email messages API currently supports updating the following email message attributes:
folderId
Update this attribute to move email messages between different folders.
seen
Update this attribute to set the seen status of the email message
One or more email message identifiers can be passed into the updateEmailMessages
method to perform a batch update of the email messages associated with those identifiers.
Email messages can only be updated in batches of 100. Supplying identifiers to the input which exceed this limit will throw a LimitExceededError
.
A BatchOperationResult
type is returned from this method call which contains the status of the batch update operation. Three possible statuses can be returned:
Success
All of the email messages succeeded to update. The return object will include a list of each email message's identifier, created and updated timestamps.
Partial
Only a subset of email messages succeeded to update. The return object will include a list of identifiers and error descriptions of email messages which failed the update and a list which succeeded the update.
Failure
All of the email messages failed to update. The return object will include a list of identifiers and error descriptions of email messages which failed the update.
// Collect the input message ids however makes sense for your implementation.
const ids = ['message-id-1', 'message-id-2']
try {
const result = await emailClient.updateEmailMessages({
ids,
values: { seen: true },
})
// `result` contains the status of the batch update operation and associated success and failure metadata.
} catch {
// Handle/notify user of error
}
Deleting Email Messages
Deleting email messages is supported as a batch operation. Deleting email messages is not the same as moving them to the trash folder. See the Updating Metadata of Email Messages section for moving email messages to the trash folder and other folders.
By deleting the email message, the message data and metadata will no longer be available and all traces will be deleted permanently.
One or more email message identifiers can be passed into the deleteEmailMessages
method to perform a batch delete of the email messages associated with those identifiers.
Email messages can only be deleted in batches of 100. Supplying identifiers to the input which exceed this limit will throw a LimitExceededError
.
A BatchOperationResult
type is returned from this method call which contains the status of the batch delete operation. Three possible statuses can be returned:
Success
All of the email messages succeeded to delete. The return object will include a list of identifiers of messages which succeeded to delete.
Partial
Only a subset of email messages succeeded to delete. The return object will include a list of identifiers of messages which failed the delete and a list which succeeded the delete.
Failure
All of the email messages failed to delete. The return object will include a list of identifiers of messages which failed to delete.
// Collect the input message ids however makes sense for your implementation.
const ids = ['message-id-1', 'message-id-2']
try {
const result = await emailClient.deleteEmailMessages(
ids
)
// `result` contains the status of the batch delete operation and associated success and failure ids.
} catch {
// Handle/notify user of error
}
Forwarded/Replied To State
When a new email message is sent, a new entity is created, with its repliedTo
and forwarded
fields set to false
by default; this indicates that it is the first message in a thread. If this new message is in reply to, or is forwarding an existing message, it references the ID of the original message. In such cases, the repliedTo
field of the original message is updated to true
if it is a reply, or the forwarded
field is updated to true
if it is a forward.
This approach helps to maintain an accurate record of the relationships between different email message entities within the conversation thread.
// Collect the original email message ID however makes sense
// for your implementation.
const messageId: string = // ...
const emailMessageHeader: InternetMessageFormatHeader = {
from: { emailAddress: "[email protected]" },
to: [{ emailAddress: "[email protected]" }],
cc: [],
bcc: [],
replyTo: [],
subject: "Example subject line",
}
To update the repliedTo
property of the original email message, set the replyingMessageId
property on the send input:
const sendInput: SendEmailMessageInput = {
senderEmailAddressId: "<sender-id>",
emailMessageHeaders: emailMessageHeader,
body: "An example email body",
attachments: [],
inlineAttachments: [],
replyingMessageId: messageId,
}
To update the forwarded
property of the original email message, set the forwardingMessageId
property on the send input:
const sendInput: SendEmailMessageInput = {
senderEmailAddressId: "<sender-id>",
emailMessageHeaders: emailMessageHeader,
body: "An example email body",
attachments: [],
inlineAttachments: [],
forwardingMessageId: messageId,
}
Once sendEmailMessage
has successfully completed, the changes to repliedTo
and/or forwarded
properties can be observed on the original email message.
try {
await emailClient.sendEmailMessage(sendInput)
const getEmailMessageInput: GetEmailMessageInput = {
id: messageId,
cachePolicy: CachePolicy.RemoteOnly
}
const emailMessage = await emailClient.getEmailMessage(
getEmailMessageInput
)
// Change(s) can be observed in
// `emailMessage.repliedTo`
// `emailMessage.forwarded`
} catch {
// ...
}
Last updated