Comment on page
Sending & Receiving Email
Provides your users the ability to perform email communications with a Sudo
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, 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
}
}
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.The user must be entitled to send emails by the
sudoplatform.email.emailMessageSendUserEntitled
entitlement. - 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.
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.
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.The user must be entitled to receive emails by the
sudoplatform.email.emailMessageReceiveUserEntitled
entitlement. - 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.
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.
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
}
}
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.
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
}
}
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.
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
}
}
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 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 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
}
}
Last modified 6mo ago