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, 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:

fileNamemimeTypecontentId

Key Attachment/s

"Secure Data n"

"application/x-sudoplatform-key"

"securekeyexchangedata@sudoplatform.com"

Body Attachment

"Secure Email"

"application/x-sudoplatform-body"

"securebody@sudoplatform.com"

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: "from@bar.com" },
    to: [{ emailAddress: "to@bar.com" }],
    cc: [{ emailAddress: "cc@bar.com" }],
    bcc: [{ emailAddress: "bcc@bar.com" }],
    replyTo: [{ emailAddress: "replyTo@bar.com" }],
    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:

  1. The user must be entitled to send emails by thesudoplatform.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.

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. The user must be entitled to receive emails by thesudoplatform.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.

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.

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:

StatusDefinition

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
}

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.

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 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:

StatusDefinition

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:

StatusDefinition

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: "from@bar.com" },
    to: [{ emailAddress: "to@bar.com" }],
    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 {
    // ...
}

No changes are made to the subject line, message body or headers; only the metadata of the email message is updated.

Last updated