Search
K
Comment on page

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.
We support using a raw RFC 822 payload to allow flexible email composition within your application.
The sendEmailMessage method is used to send an email message. The first input property, rfc822Data, takes in the email message contents formatted under RFC 822 as an array of bytes. The second input property, senderEmailAddressId, must match the id of the email address from the from field in the RFC 822 data. A call to this method returns the id of the sent email message.
A single email message can be sent to a maxium of 10 recipients at once by default. Adding recipients to the email message which exceed this limit will throw a LimitExceededError.
TypeScript
Swift
Kotlin
// Collect the input RFC 822 data and sender email address id however makes sense for your implementation.
const rfc822Data: ArrayBuffer = // ...
const senderEmailAddressId = senderEmailAddress.id
try {
const messageId = await emailClient.sendEmailMessage({
rfc822Data,
senderEmailAddressId,
})
// `messageId` is the identifier associated with the sent email message. You can use this to access the data of the email message.
} catch {
// Handle/notify user of error
}
/// Collect the input RFC822 data and email address id however makes sense for your implementation.
let rfc822Data = input.rfc822Data
let emailAddressId = input.emailAddressId
let sendInput = SendEmailMessageInput(
rfc822Data: rfc822Data,
senderEmailAddressId: emailAddressId
)
do {
let id = try await emailClient.sendEmailMessage(
withInput: sendInput
)
/// `id` is the identifier associated with the sent email message. You can use this to access the data of the email message.
} catch {
/// Handle/notify user of error
}
// Collect the input RFC 822 data and sender email address id however makes sense for your implementation.
val rfc822EncodedMessage: ByteArray = // ...
val senderEmailAddressId: String = // ...
launch {
try {
val input = SendEmailMessageInput(
rfc822Data = rfc822Data,
senderEmailAddressId = senderEmailAddressId
)
val messageId = withContext(Dispatchers.IO) {
emailClient.sendEmailMessage(input)
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}

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:
  1. 1.
    The user must be entitled to send emails by thesudoplatform.email.emailMessageSendUserEntitled entitlement.
  2. 2.
    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.
See Email Entitlements for an overview of how the Email service integrates with the Sudo Platform Entitlements system.

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:
  1. 1.
    The user must be entitled to receive emails by thesudoplatform.email.emailMessageReceiveUserEntitled entitlement.
  2. 2.
    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.
See Email Entitlements for an overview of how the Email service integrates with the Sudo Platform Entitlements system.

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).
These retrieval methods return the email message metadata but not the content of the message. Email message metadata consists of the information such as: addressees, sender, subject, folder, timestamps, direction, whether or not it has attachments.
To learn how to access the RFC 822 message content of the email message, see Accessing RFC 822 Message Data.
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.
TypeScript
Swift
Kotlin
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
}
/// Collect the input id of the email message object.
let input = GetEmailMessageInput(
id: input.messageId,
cachePolicy: .remoteOnly
)
do {
let emailMessage = try await emailClient.getEmailMessage(
withInput: input
)
// `emailMessage` contains the email message object, else `nil` if not found.
} catch {
// Handle/notify user of errors
}
val emailMessageId = emailMessage.id
launch {
try {
val input = GetEmailMessageInput(
id = emailMessageId,
cachePolicy = CachePolicy.REMOTE_ONLY
)
val emailMessage = withContext(Dispatchers.IO) {
emailClient.getEmailMessage(input)
}
// [emailMessage] contains the email message object, else [null] if not found.
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}

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:
Status
Definition
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.

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.
TypeScript
Swift
Kotlin
// Collect the input email address id however makes sense for your implementation.
const emailAddressId = emailAddress.id
try {
const result = await emailClient.listEmailMessagesForEmailAddressId({
emailAddressId,
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
}
// Collect the input email address id however makes sense for your implementation.
let emailAddressId = emailAddress.id
let dateRange = DateRange(
startDate: Date(millisecondsSince1970: 1.0),
endDate: Date()
)
let input = ListEmailMessagesForEmailAddressInput(
emailAddressId: emailAddressId,
cachePolicy: .remoteOnly,
dateRange: dateRange,
sortOrder: .asc,
limit: 10,
nextToken: nil
)
do {
let result = try await emailClient.listEmailMessagesForEmailAddressId(
withInput: input
)
switch result {
case .success(let successResult):
// successResult.items contains an array of the email messages for this email address.
// successResult.nextToken will not be `nil` if there are more email messages to retrieve.
case .partial(let partialResult):
// partialResult.items contains an array of the email messages successfully
// retrieved and unsealed for this email address.
// partialResult.failed contains an array of email messages retrieved
// for this email address, but unsuccessfully unsealed.
// partialResult.nextToken will not be `nil` if there are more email messages to retrieve
}
} catch {
// Handle/notify user of errors
}
// Collect the input email address id however makes sense for your implementation.
val emailAddressId = emailAddress.id
val dateRange = DateRange(
startDate = // startDate as a [Date]
endDate = // endDate as a [Date]
)
launch {
try {
val input = ListEmailMessagesForEmailAddressIdInput(
emailAddressId = emailAddressId,
cachePolicy = CachePolicy.REMOTE_ONLY,
dateRange = dateRange,
sortOrder = SortOrder.DESC,
limit = 20,
nextToken = nextToken
)
val result = withContext(Dispatchers.IO) {
emailClient.listEmailMessagesForEmailAddressId(input)
}
when (result) {
is ListAPIResult.Success -> {
// [result.items] contains the list of items matching the input.
// Page through the results if [output.nextToken] != null.
}
is ListAPIResult.Partial -> {
// [result.items] contains the list of items matching the input that decrypted successfully.
// [result.failed] contains the list of items that failed decryption with associated error.
// Page through the results if [partial.nextToken] != null.
}
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}

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.
TypeScript
Swift
Kotlin
// Collect the input email folder id however makes sense for your implementation.
const emailFolderId = emailFolder.id
try {
const result = await emailClient.listEmailMessagesForEmailFolderId({
emailFolderId,
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
}
// Collect the input email folder id however makes sense for your implementation.
let emailFolderId = emailFolder.id
let dateRange = DateRange(
startDate: Date(millisecondsSince1970: 1.0),
endDate: Date()
)
let input = ListEmailMessagesForEmailFolderIdInput(
emailFolderId: emailFolderId,
cachePolicy: .remoteOnly,
dateRange: dateRange,
sortOrder: .asc,
limit: 10,
nextToken: nil
)
do {
let result = try await emailClient.listEmailMessagesForEmailFolderId(
withInput: input
)
switch result {
case .success(let successResult):
// successResult.items contains an array of the email messages for this email folder.
// successResult.nextToken will not be `nil` if there are more email messages to retrieve.
case .partial(let partialResult):
// partialResult.items contains an array of the email messages successfully
// retrieved and unsealed for this email folder.
// partialResult.failed contains an array of email messages retrieved
// for this email folder, but unsuccessfully unsealed.
// partialResult.nextToken will not be `nil` if there are more email messages to retrieve
}
} catch {
// Handle/notify user of errors
}
// Collect the input email folder id however makes sense for your implementation.
val emailFolderId = emailFolder.id
val dateRange = DateRange(
startDate = // startDate as a [Date]
endDate = // endDate as a [Date]
)
launch {
try {
val input = ListEmailMessagesForEmailFolderIdInput(
folderId = emailFolderId,
cachePolicy = CachePolicy.REMOTE_ONLY,
dateRange = dateRange,
sortOrder = SortOrder.DESC,
limit = 20,
nextToken = nextToken
)
val result = withContext(Dispatchers.IO) {
emailClient.listEmailMessagesForEmailFolderId(input)
}
when (result) {
is ListAPIResult.Success -> {
// [result.items] contains the list of items matching the input.
// Page through the results if [output.nextToken] != null.
}
is ListAPIResult.Partial -> {
// [result.items] contains the list of items matching the input that decrypted successfully.
// [result.failed] contains the list of items that failed decryption with associated error.
// Page through the results if [partial.nextToken] != null.
}
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}
By default, list email messages API have a limit of 1MB of data when no limit is supplied.

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.
TypeScript
Swift
Kotlin
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
}
// Collect the input email message and email address ids however makes sense for your implementation.
let emailMessageId = emailMessage.id
let emailAddressId = emailAddress.id
let input = GetEmailMessageRfc822DataInput(
id: emailMessageId,
emailAddressId: emailAddressId
)
do {
let rfc822Data = try await emailClient.getEmailMessageRfc822Data(
withInput: input
)
// rfc822Data contains the raw RFC 6854 (supersedes RFC 822) data of
// an email message associated with the `emailMessageId`.
} catch {
// Handle/notify user of error
}
// Collect the input email message and email address ids however makes sense for your implementation.
val emailMessageId = emailMessage.id
val emailAddressId = emailAddress.id
launch {
try {
val input = GetEmailMessageRfc822Data(
id = emailMessageId,
emailAddressId = emailAddressId
)
val rfc822Data = withContext(Dispatchers.IO) {
emailClient.getEmailMessageRfc822Data(input)
}
// If the [id] matches an email message, [rfc822Data] will be returned, else [null]
if (rfc822Data != null) {
val emailMessageWithBody = // parse the [rfc822Data]
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}

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.
Swift
Kotlin
To subscribe to email messages, use the subscribeToEmailMessageCreated(withDirection:resultHandler:) method for handling created message events, or subscribeToEmailMessageDeleted(resultHandler:) for handling deleted message events. These methods use a closure resultHandler to allow you to decide what happens when a message is received in real-time.
A SubscriptionToken is also returned from the call to these method. In order to ensure that the subscription remains established and connected, you must keep a strong reference to this returned object. If the reference is lost and marked for clean-up in ARC, the subscription will be cancelled and closed.
Setting Up A Subscription
To setup the subscription for created events, call the subscription method as so:
let token = try await emailClient.subscribeToEmailMessageCreated(
withDirection: .inbound
) { result in
switch result {
case let .failure(error):
// Handle error
case let .success(emailMessage):
// `emailMessage` is the new email message object received in real time.
}
To setup the subscription for deleted events, call the subscription method as so:
let token = try await emailClient.subscribeToEmailMessageDeleted { result in
switch result {
case let .failure(error):
// Handle error
case let .success(emailMessage):
// `emailMessage` is the deleted email message object received in real time.
}
It is important to ensure that token is not lost until the subscription is no longer needed.
Any errors that arise during the setup of the subscription will be returned via a thrown exception, so make sure to wrap your calls in a do-catch block to handle any failed behavior.
Cancelling A Subscription
To cancel a subscription, use the cancel() method on the SubscriptionToken. For example:
token.cancel()
This will ensure that the subscription is cancelled and system resources are freed.
To subscribe to email messages, use the subscribeToEmailMessages method. This method accepts a unique subscription identifier and a subscriber object which must implement the emailMessageChanged(emailMessage: EmailMessage) method. This handler should contain application-specific implementation of behavior in the event of a email message change. The subscription object should also implement the connectionStatusChanged(state: ConnectionState) method which is invoked in the event of subscription connection state changes between CONNECTED and DISCONNECTED. This handler allows the consumer to detect when the subscription connection has been lost and take appropriate corrective action.
Setting Up A Subscription
launch {
try {
withContext(Dispatchers.IO) {
emailClient.subscribeToEmailMessages('subscription-id', object : EmailMessageSubscriber {
override fun emailMessageChanged(emailMessage: EmailMessage) {
// Implement handling for email message changed behaviour
}
override fun connectionStatusChanged(state: Subscriber.ConnectionState) {
// Implement handling for subscription connection status changed
}
})
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}
Cancelling A Subscription
To cancel a subscription, use the unsubscribeFromEmailMessages or unsubscribeAllFromEmailMessages method. For example:
val subscriptionId = // From where the subscription was set up
launch {
try {
emailClient.unsubscribeFromEmailMessages(subscriptionId)
// or
emailClient.unsubscribeAllFromEmailMessages()
} catch (e: EmailMessageException) {
// Handle/notify the user of exception
}
}
This will ensure that the subscription is cancelled and system resources are freed.
If the subscribeToEmailMessages 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:
Status
Definition
Success
All of the email messages succeeded to update.
Partial
Only a subset of email messages succeeded to update. The return object will include a list of identifiers of messages which failed the update and a list which succeeded the update.
Failure
All of the email messages failed to update.
TypeScript
Swift
Kotlin
// Collect the input message ids however makes sense for your implementation.
const ids = ['message-id-1', 'message-id-2']
try {
const updateResult = await emailClient.updateEmailMessages({
ids,
values: { seen: true },
})
// `updateResult` contains the status of the batch update operation.
} catch {
// Handle/notify user of error
}
/// Collect the input message ids however makes sense for your implementation.
let ids = ['message-id-1', 'message-id-2']
let input = UpdateEmailMessagesInput(
ids: ids,
values: UpdateEmailMessagesValues(
folderId: "\(emailAddressId)-TRASH",
seen: true
)
)
do {
let result = try await emailClient.updateEmailMessages(withInput: input)
switch result {
case .success:
// All email messages updated successfully
case. partial:
// `result.successItems` contains an array of all the email message IDs
// that were updated successfully.
// `result.failureItems` cotains an array of all the email message IDs
// that failed to update successfully.
case .failure:
// All email messages failed to update.
}
} catch {
/// Handle/notify user of error
}
// Collect the input message ids however makes sense for your implementation.
val ids = listOf("message-id-1", "message-id-2")
launch {
try {
val input = UpdateEmailMessagesInput(
ids = ids,
values = UpdateEmailMessagesInput.UpdatableValues(
folderId = "$emailAddressId-TRASH",
seen = true
)
)
val result = withContext(Dispatchers.IO) {
emailClient.updateEmailMessages(input)
}
when (result) {
is BatchOperationResult.SuccessOrFailureResult -> {
// [result.status] contains the status of the batch update operation.
}
is BatchOperationResult.Partial -> {
// [result.successValues] contains the list of items in which the update operation succeeded.
// [result.failureValues] contains the list of items in which the update operation failed.
}
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}

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:
Status
Definition
Success
All of the email messages 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.
TypeScript
Swift
Kotlin
// Collect the input message ids however makes sense for your implementation.
const ids = ['message-id-1', 'message-id-2']
try {
const deleteResult = await emailClient.deleteEmailMessages(
ids
)
// `deleteResult` contains the status of the batch delete operation.
} catch {
// Handle/notify user of error
}
/// Collect the email message ids however makes sense for your implementation.
let ids = ['message-id-1', 'message-id-2']
do {
let result = try await self.emailClient.deleteEmailMessages(
withIds: ids
)
switch result {
case .success:
// All email messages were deleted successfully
case. partial:
// `result.successItems` contains an array of all the email message IDs
// that were deleted successfully.
// `result.failureItems` cotains an array of all the email message IDs
// that failed to be deleted.
case .failure:
// All email messages failed to be deleted.
}
} catch {
/// Handle/notify user of error
}
// Collect the input message ids however makes sense for your implementation.
val ids = listOf("message-id-", "message-id-2")
launch {
try {
val result = withContext(Dispatchers.IO) {
emailClient.deleteEmailMessages(ids)
}
when (result) {
is BatchOperationResult.SuccessOrFailureResult -> {
// [result.status] contains the status of the batch delete operation.
}
is BatchOperationResult.Partial -> {
// [result.successValues] contains the list of items in which the delete operation succeeded.
// [result.failureValues] contains the list of items in which the delete operation failed.
}
}
} catch (e: EmailMessageException) {
// Handle/notify user of exception
}
}