The essentials to give your users the ability to secure their real email addresses behind Email Masks
Email Masks can be provisioned for different configured domains to provide more options to your users.
To provision an Email Mask, first check if it is valid and available. Once the address is chosen and confirmed, it can be used to provision the Email Mask.
Get Email Mask Domains
Before checking for the availability of an address for an Email Mask for your user, a valid mask domain is needed. The Sudo Platform Email Service can be configured to support multiple domains for this purpose.
Each project in your Sudo Platform account can be configured to support a set of email domains. Your solutions engineer can assist you in configuring DNS entries for domains you own or you can have the Sudo Platform do that on your behalf.
A call to the getEmailMaskDomains method provides a list of domains that are available for provisioning Email Masks.
try{constmaskDomains=awaitemailClient.getEmailMaskDomains() // `maskDomains` contains an array of supported domains to be used for provisioning Email Masks}catch (e) { // Handle/notify user of errors}
do {let maskDomains =tryawait client.getEmailMaskDomains() // `maskDomains` contains alist of domains available for provisioning Email Maskscatch{ /// Handle/notify user of errors}
launch {try {val maskDomains =withContext(Dispatchers.IO) { emailClient.getEmailMaskDomains() }// [maskDomains] contains a list of configured domains for provisioning Email Masks. } catch (e: EmailAddressException) {// Handle/notify user of exception } }
Check Email Mask Availability
In order to ensure your users can choose a unique, available, address for their Email Mask, you can use the same checkEmailAddressAvailability method as you would for a regular Sudo Platform email address. More information on that method can be found here.
Provision Email Mask
Once your user has the address they would like to use for their Email Mask, it can be provisioned. The provisionEmailMask method will return the newly provisioned Email Mask object.
An ownershipProofToken is required as part of the input. This ties together the Sudo and Email Mask such that the Sudo becomes the owner of the Mask. Use the getOwnershipProof method on the SudoProfilesClient in the SudoProfiles SDK in order to obtain an ownershipProofToken. See the Sudo section for more information.
Real Address Ownership
The realAddress property of the input must be the exact local-part@domain email address that the mask should forward to. For Sudo Platform Email Addresses, the address must be owned by the same user trying to provision the Mask.
If, however, you decide to enable the option to allow your users to set up Masks for external email addresses, then the Mask that gets provisioned will initially have its state set to PENDING. While in this state, it cannot receive messages. In order to change the state to ENABLED, the user must verify ownership of their external email address. See Verifying External Email Addresses.
Entitlements
In order to be able to provision an Email Mask, the user must be entitled. The following entitlement checks are performed:
The user is entitled to provision Email Masks by the sudoplatform.email.emailMaskUserEntitled entitlement.
The user against which the new email address is to be provisioned has at least 1 remaining sudoplatform.email.emailMaskMaxPerUser entitlement to consume.
If these entitlements' checks do not succeed, the provisionEmailMask API fails with an insufficient entitlements error.
See Email Entitlements for an overview of how the Email service integrates with the Sudo Platform Entitlements system.
Optional Parameters
The provisionEmailMask method's input object can take up to two additional optional parameters:
expiresAt - If set, the mask will automatically be deprovisioned at the given date.
metadata - This is a string to string map that can be used to store information about the Email Mask such as the purpose of it. The data in this property gets encrypted with the user's default key pair before being sent to the server, and decrypted upon retrieval.
Both of these optional properties can be updated, or deleted via the updateEmailMask method.
Deprovision Email Mask
An Email Mask can be deprovisioned using the deprovisionEmailMask method by passing in the id of an existing Email Mask object.
By deprovisioning an Email Mask, the user will no longer receive messages sent to that Mask, and a bounce will be returned to anyone sending messages to it.
Updating an Email Mask
An existing email mask can have a subset of its data updated by the user by calling the updateEmailMask method. The properties that can be updated are outlined in the table below.
Property
To ignore
To delete
expiresAt
TypeScript: undefined (or exclude from object)
Swift: nil
Kotlin: null
TypeScript: undefined (or exclude from object)
Swift: nil
Kotlin: null
Typescript: null
Swift: [:]
Kotlin: emptyMap()
Retrieving Existing Email Masks
Previously provisioned Email Masks can be retrieved as a paginated list via the listEmailMasksForOwner method.
A call to the API will return a ListOutput<EmailMask> object. The items property will contain a list of the masks and the nextToken property will contain a token that can be used for retrieving the next page of results if there are more.
The list can also be filtered by passing in an optional filter property to the method call. The call can be filtered by status and/or realAddressType. Each of those must further be specified as either equal, oneOf, notEqual or notOneOf.
The list can also be given a limit on how many results to return. By default, this limit is 10.
Disabling and Enabling Email Masks
An Email Mask can be disabled by the user to temporarily stop it from receiving emails without fully deprovisioning it. When in the DISABLED status, messages sent to the mask will not be delivered to the real address, and the sender will not receive a bounce in response until it is enabled again.
const maskAddress: string = // The email address to use for the Mask, checked via the checkEmailAddressAvailability method
const ownershipProofToken: string = // Found via the get ownership proof API (See "Sudos" section)
const realAddress: string = // The real email address that the Mask will forward to
try {
const emailMask = await emailClient.provisionEmailMask({
maskAddress,
realAddress,
ownershipProofToken,
})
} catch (e) {
// Handle/notify user of errors
}
let maskAddress: String = /// The email address to use for the Mask, checked via the checkEmailAddressAvailability method
let ownershipProofToken: String = /// Found via the get ownership proof API (See "Sudos" section)
let realAddress: String = /// The real email address that the Mask will forward to
do {
let provisionEmailMaskInput = ProvisionEmailMaskInput(
maskAddress: maskAddress,
ownershipProofToken: ownershipProofToken,
realAddress: realAddress
)
let emailMask = try await emailClient.provisionEmailMask(
withInput: provisionEmailMaskInput
)
/// `emailMask` contains the newly provisioned Email Mask
} catch {
/// Handle/notify user of error
}
val maskAddress: String = /// The email address to use for the Mask, checked via the checkEmailAddressAvailability method
val ownershipProofToken: String = /// Found via the get ownership proof API (See "Sudos" section)
val realAddress: String = /// The real email address that the Mask will forward to
launch {
try {
val input = ProvisionEmailMaskInput(
maskAddress = maskAddress,
ownershipProofToken = ownershipProofToken,
realAddress = realAddress,
)
val emailMask = withContext(Dispatchers.IO) {
emailClient.provisionEmailMask(input)
}
// The returned [emailMask] has been provisioned
} catch (e: EmailAddressException) {
// Handle/notify user of exception
}
}
const emailMaskId: string = emailMask.id // Collect the Email Mask however makes sense for your application
try {
const deprovisionedEmailMask = await emailClient.deprovisionEmailMask({
emailMaskId: emailMaskId
})
} catch (e) {
// Handle/notify user of errors
}
/// Collect the input email mask id however makes sense for your implementation.
let emailMaskId = emailMask.id
do {
let deprovisionEmailMaskInput = DeprovisionEmailMaskInput(
emailMaskId: emailMaskId
)
let deprovisionedEmailMask = try await emailClient.deprovisionEmailMask(
withInput: deprovisionEmailMaskInput
)
/// `deprovisionedEmailMask` is the record that was just deprovisioned.
} catch {
/// Handle/notify user of error
}
/// Collect the input email mask id however makes sense for your implementation.
val emailMaskId = emailMask.id
launch {
try {
val input = DeprovisionEmailMaskInput(
emailMaskId = emailMaskId
)
val emailMask = withContext(Dispatchers.IO) {
emailClient.deprovisionEmailMask(input)
}
// The returned [emailMask] has been deprovisioned
} catch (e: EmailAddressException) {
// Handle/notify user of exception
}
}
const emailMaskId: string = emailMask.id // Collect the Email Mask however makes sense for your application
try {
const metadata = { prop1: 'abc', prop2: 123 }
const expiresAt = new Date() // Construct the expiry date however makes sense
let updatedMask = await emailClient.updateEmailMask({
emailMaskId: emailMaskId,
metadata: metadata,
expiresAt: expiresAt,
})
// The Email Mask now has the updated properties
updatedMask = await emailClient.updateEmailMask({
emailMaskId: emailMaskId,
metadata: null,
expiresAt: null,
})
// The Email Mask no longer has the `metadata` or `expiresAt` set
} catch (e) {
// Handle/notify user of errors
}
// Collect the input email mask id however makes sense for your implementation.
let emailMaskId = emailMask.id
do {
let metadata = [ "prop1": "abc", "prop2": String(123) ]
let expiresAt = Date() /// Construct the expiry date however makes sense
var input = UpdateEmailMaskInput(
emailMaskId: emailMaskId,
metadata: metadata,
expiresAt: expiresAt
)
var updatedMask = try await emailClient.updateEmailMask(withInput: input)
// The Email Mask now contains the updated properties
input = UpdateEmailMaskInput(
emailMaskId: emailMaskId,
metadata: [:],
expiresAt: Date(timeIntervalSince1970: 0)
)
updatedMask = try await emailClient.updateEmailMask(withInput: input)
// The Email Mask no longer has metadata or expiresAt
} catch {
/// Handle/notify user of error
}
/// Collect the input email mask id however makes sense for your implementation.
val emailMaskId = emailMask.id
launch {
try {
val metadata = mapOf(
"prop1" to "abc",
"prop2" to "${123}"
)
val expiresAt = Date() // Construct the expiry date however makes sense
var input = UpdateEmailMaskInput(
emailMaskId = emailMaskId,
metadata = metadata,
expiresAt = expiresAt
)
var updatedMask = withContext(Dispatchers.IO) {
emailClient.updateEmailMask(input)
}
// The Email Mask now contains the updated properties
input = UpdateEmailMaskInput(
emailMaskId = emailMaskId,
metadata = emptyMap(),
expiresAt = Date(0)
)
updatedMask = withContext(Dispatchers.IO) {
emailClient.updateEmailMask(input)
}
// The Email Mask no longer has metadata or expiresAt
} catch (e: EmailAddressException) {
// Handle/notify user of exception
}
}
const emailMaskId: string = emailMask.id // Collect the Email Mask however makes sense for your application
try {
const result = await emailClient.listEmailMasksForOwner({
filter: {
status: { notOneOf: [EmailMaskStatus.PENDING, EmailMaskStatus.LOCKED] },
realAddressType: { equal: EmailMaskRealAddressType.INTERNAL },
}
}
// `result.items` contains a list of Masks that are not PENDING or LOCKED and have a real
// address type of INTERNAL
// Page through the results if result.nextToken != undefined.
} catch (e) {
// Handle/notify user of errors
}
// Collect the input email mask id however makes sense for your implementation.
let emailMaskId = emailMask.id
do {
let filter = EmailMaskFilter(
status: .notOneOf([.pending, .locked]),
realAddressType: .equal(.internal),
)
let input = ListEmailMasksForOwnerInput(
filter: filter
)
let result = try await emailClient.listEmailMasksForOwner(withInput: input)
// `result.items` contains a list of Masks that are not PENDING or LOCKED and have a real
// address type of INTERNAL
// Page through the results if result.nextToken != nil.
} catch {
/// Handle/notify user of error
}
/// Collect the input email mask id however makes sense for your implementation.
val emailMaskId = emailMask.id
launch {
try {
val filter = EmailMaskFilterInput(
status = NotOneOfStatusFilter(
listOf(EmailMaskStatus.PENDING, EmailMaskStatus.LOCKED)
),
realAddressType = EqualRealAddressTypeFilter(EmailMaskRealAddressType.INTERNAL)
)
val input = ListEmailMasksForOwnerInput(filter = filter)
val result = withContext(Dispatchers.IO) {
emailClient.listEmailMasksForOwner(input)
}
// `result.items` contains a list of Masks that are not PENDING or LOCKED and have a real
// address type of INTERNAL
// Page through the results if result.nextToken != null.
} catch (e: EmailAddressException) {
// Handle/notify user of exception
}
}
const emailMaskId: string = emailMask.id // Collect the Email Mask however makes sense for your application
try {
const disabledMask = await emailClient.disableEmailMask({
emailMaskId: emailMaskId,
})
// Mask is now disabled; disabledMask.status == EmailMaskStatus.DISABLED
const enabledMask = await emailClient.enableEmailMask({
emailMaskId: emailMaskId,
})
// Mask is now enabled; enabledMask.status == EmailMaskStatus.ENABLED
} catch (e) {
// Handle/notify user of errors
}
// Collect the input email mask id however makes sense for your implementation.
let emailMaskId = emailMask.id
do {
let disableInput = DisableEmailMaskInput(emailMaskId: emailMaskId)
let disabledMask = try await client.disableEmailMask(withInput: disableInput)
/// Email Mask is now disabled; disabledMask.status == .disabled
let enableInput = EnableEmailMaskInput(emailMaskId: emailMaskId)
let enabledMask = try await client.enableEmailMask(withInput: enableInput)
/// Email Mask is now enabled; disabledMask.status == .enabled
} catch {
/// Handle/notify user of error
}
/// Collect the input email mask id however makes sense for your implementation.
val emailMaskId = emailMask.id
launch {
try {
val disableInput = DisableEmailMaskInput(emailMaskId)
val disabledMask = withContext(Dispatchers.IO) {
emailClient.disableEmailMask(disableInput)
}
// Email Mask is now disabled; disabledMask.status == EmailMaskStatus.DISABLED
val enableInput = EnableEmailMaskInput(emailMaskId)
val enabledMask = withContext(Dispatchers.IO) {
emailClient.enableEmailMask(disableInput)
}
// Email Mask is now enabled; disabledMask.status == EmailMaskStatus.ENABLED
} catch (e: EmailAddressException) {
// Handle/notify user of exception
}
}