Skip to main content

Mail

1. Overview

Mail is a service that helps developers send in-game mails with attachments to players asynchronously through the PGOS web portal or virtual server. Game client can pull mails, mark mails, delete mails and claim the attachment in mails.

Mail can be applied in the following scenarios:

  1. System Notification: Developers can update notices to players by sending them mails.
  2. Campaign Events: Developers can configure event triggers for achievements to distribute rewards to players.
  3. Gifts(not supported yet): A player can purchase in-game items and give them to another player as a gift in the form of sending the player a mail with the items as attachment.

2. Key Concepts

There are three key concepts in mail service:

  1. Mailbox: Each player has a mailbox once the player registered on PGOS.
  2. Mail: Mail can be sent to players with real time notification. The mails will be saved in the backend for 3 months. During the validity period, emails can be retrieved and managed by their recipients.
  3. Attachment: Each mail can be attached with any number of items and currencies. The player can obtain these items and currencies by claiming the attachment.
tip

All mails are kept on backend for by default 90 days. Expired mails will not be viewed or operated. If the time limit needs to be modified, please contact us.

3. Send a Mail

3.1 Send a Mail on the Web Portal

Click the Engagement menu, and you will see the following page.

image-20240918111342995

Field Description:

  • Receivers:the receivers of a mail (at most 1000 players can be selected at a time);
    • Specified Players: send this mail to limited amount of players
    • Global: send this mail to all players
  • Category: the category of mail info, eg: System, Notice, Campaign, Award, Gift, Custom;
  • Custom Data: the custom data of mail information. You can define some data and pass it to the SDK;
  • Title & Content: the content of a mail. You can define the title and content information in multiple languages;
  • Attachments: the attachment(s) in a mail. You can choose in-game item and currency from economy part.

How to Choose Receivers

Send to specified players

Click the + Add button on the right side of Receivers

image-20240918111426625

Select the mail recipient in the pop-up dialog. You can also add the receiver by searching Player ID.

image-20220620151658197

Send to all players

If global scope is selected, an expiration time must be specified.

image-20240918111529415

tip

When the receivers' scope is set to Global, all players will receive the email. Due to the potentially large number of online players, receiving a Global type email may be delayed by 1-2 minutes. For offline players, the email will be delivered when they log into the game.

Global type mail will only be delivered to players who have logged in or remained online before the deadline. Once the deadline has passed, players who have already received the global mail will keep it, but players who log in after this point will no longer receive the mail.

How to Add Title & Content

Click the + Add Language button on the bottom of Title & Content, and enter the mail content in the pop-up dialog.

image-20220620152341853

  • Language: PGOS provides most of the language types by default, and you can also add language types:
  • Title: The subject of the mail with a maximum length of 1024 characters;
  • Content: The content of the mail with a maximum length of 4096 characters;
tip

In the Content, you can use the placeholder {{receiver_name}} to represent the receivers' player name. Such as:

Hello {{receiver_name}}, mail contents ......

The first Language you add will be set as the default language, and you can change it.

image-20220620155056054

How to Add Attachments

Click the + Add Attachment button on the bottom of Attachments, and choose the mail attachments in the pop-up dialog.

image-20220620155236599

After selecting the In-game Items and Currency associated with the mail, you can set their amount. The attachment's amount can not be less than 1 or bigger then 32-bit unsigned integer (4,294,967,295).

image-20220620155353719

Send a Mail

After confirming that the all informtion is correct, click the Send button at the bottom to send the mail.

Import from Mail Template

You can click Import from Mail Template to quickly import the Mail Template to fill in the data. How to create a Mail Template, please click here.

image-20220620155823979

3.2 Send a Mail via Virtual Server

The Virtual Server can utilize the following HTTP API endpoints listed in the following table to implement functionalities such as sending and recalling emails.

InterfaceDescription
SendMailSend mail to players. Specify receivers, multi-language contents and attachments in requests.
SendGlobalMailSend global mail to all players.
SendMailWithTemplateSend mail with template. Configure template on web portal first.
SendGlobalMailWithTemplateSend global mail with template to all players.
GetPlayerMailsGet player' mails. This interface supports pagination.
GetDeletedPlayerMailsGet player' deleted mails (deleted mails will be reserved for 3 months).
RecoverDeletedMailsRecover deleted mails for specified player.
RetractGlobalMailRetract a previously sent global mail.

4. Mail Template

Switch the top tab to Mail Template, and all the Mail Templates will be displayed.

image-20220620161931224

Create Mail Template

Click the + Add Mail Template button, as shown below:

image-20220620161556672

Remove Mail Templates

Select the Mail Template you want to delete, click the Delete Mail Template button at the bottom, and click Submit in the pop-up dialog to delete it.

image-20220620162712305

Modify/Clone Mail Template

Select the Mail Template and then click the edit or clone button.

image-20220629155936935

5. Mail Env Variables

You could use mail variables to customize the content of mails that generated when sent. PGOS provides a number of predefined environment variables:

  • {{receiver_name}}. Will be replaced with the display name of the mail receiver player.
  • {{goal_name}}. This is an available that could be used in mails sent by PGOS Goals Service and will be replaced with the name of the player goal that triggered the mail.
  • {{cur_tier_order}}. This is an available that could be used in the email sent by PGOS Goals Service and will be replaced with the current tier of a hyper goal.

All available variables can be viewed in Engagement>Mails>Mail Env Variables of portal.

In addition, mail environment variables are allowed to be added and used when send mail.

image-20230620173353274

6. Manage Mails

This chapter mainly introduces the client APIs.

Note:

When sending a mail, the title and content of the mail can be configured in multiple languages, and one of them is the default language. The language type of mails received by players is determined according to the language set by themselves:

  • If the multiple languages of the mail contains the player's language, the mail corresponding to the player's language will be returned.
  • If there is no language that players use in the multiple languages of the mail, the mail corresponding to the default language will be returned.

Developers can update player's language by calling UPgosPlayerProfileAPI:SetMyLanguage API.

6.1 Data Structure

FClientMailInfo is designed to describe a mail. Key fields in FClientMailInfo are as follows:

  • mail_id: The unique identifier of a mail.
  • title: The title of the mail.
  • category: The category of the mail.
    • System: System mail.
    • Notice: Game notice mail.
    • Campaign: Game campaign mail.
    • Award: Used to issue awards to players.
    • Gift: A player can purchase in-game items and give to another player as gift in the form of sending the player a mail with the items as an attachment.
    • Custom: Custom category, if the above categories are not satisfied, developer can use this to represent a category.
  • content: The content of the mail.
  • attachment: The attachment of the mail, including In-game Items and Currency.
  • custom_data: Custom data for mail.
  • is_read: Whether the mail has been read.
  • sent_time: The time a mail was sent.

FClientMailAttachmentInfo is designed to describe a mail attachment. Key fields in FClientMailAttachmentInfo are as follows:

  • items: An array of In-game Items waiting to be claimed.
  • currencies: An array of Currencies waiting to be claimed.
  • is_gift_from_player: Whether the attachment was gifted by another player.
  • giver: If is_gift_from_player is true, 'giver' represents the player who give the gift.
  • is_claimed: Whether the attachment has been claimed.

6.2 Get Mail List

Players can paginate to get their mail list by calling UPgosMailAPI::GetMailList API. Players can specify the category of the mail when get it, and EClientMailCategory::All represents all categorys of mail.

Interface prototype:

/**
* Get mail list by category, category 'All' represents all categories.
*
* @param Params Request parameters for get mail list.
*/
void GetMailList(
const FClientGetMailListParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientGetMailListResult* Data)> Callback) const;

/** Request parameters for get mail list. */
struct FClientGetMailListParams {
/** Query the specified category. */
MailCategory category;
/** The starting position of the list. */
uint32_t offset = 0;
/** The query count. */
uint32_t count = 0;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
FClientGetMailListParams Params;
// Fill get mail list params
Mail->GetMailList(Params, [](const FPgosResult& Ret, const FClientGetMailListResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetMailList Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetMailList Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.3 Mark Mails as Read

Players can mark mails as read by calling UPgosMailAPI::MarkMailAsReadById/UPgosMailAPI::MarkMailAsReadByCategory API. IT respectively means to mark the mail status by id and category.

In UPgosMailAPI::MarkMailAsReadByCategory API, EClientMailCategory::All represents all categorys.

Interface prototype:

/**
* Mark mail as read by id.
*
* @param MailIds Mail IDs.
*/
void MarkMailAsReadById(
const TArray<FString>& MailIds,
TFunction<void(const FPgosResult& Ret)> Callback) const;

/**
* Mark mail as read by category.
*
* @param Category Mail category, category 'All' represents all categories.
*/
void MarkMailAsReadByCategory(
EClientMailCategory Category,
TFunction<void(const FPgosResult& Ret)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
TArray<FString> MailIds;
Mail->MarkMailAsReadById(MailIds, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("MarkMailAsReadById Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("MarkMailAsReadById Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

void SomeUObjectClass::SomeFunction1()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
EClientMailCategory Category;
// specify mail category
Mail->MarkMailAsReadByCategory(Category, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("MarkMailAsReadByCategory Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("MarkMailAsReadByCategory Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.4 Claim Attachments in a Mail

Players can claim mail attachments by calling UPgosMailAPI::ClaimMailAttachmentById/UPgosMailAPI::ClaimMailAttachmentByCategory API. After the attachments is successfully claimed, players can find the item instances in player inventory and currencies in balances.

In UPgosMailAPI::ClaimMailAttachmentByCategory API, EClientMailCategory::All represents all categorys.

An unread mail will be automatically marked as read when its attachment is successfully claimed.

The returned results are saved in the FClientClaimMailAttachmentResult structure:

struct FClientClaimMailAttachmentResult 
{
/** The item instances obtained after claim attachments. */
FItemInstPack item_insts;
/** The currencies obtained after claim attachments. */
TArray<FCurrency> currencies;
/** Unique items that already in inventory cause failed to be granted. */
TArray<FUnclaimedItem> conflict_unique_items;
/** Mails that failed to claim attachments, key: mail ID, value: failed reason. */
TMap<FString, FString> fails;
};

Interface prototype:

/**
* Claim mail attachments by mail ID.
*
* @param MailIds Mail IDs.
*/
void ClaimMailAttachmentById(
const TArray<FString>& MailIds,
TFunction<void(const FPgosResult& Ret, const FClientClaimMailAttachmentResult* Data)> Callback) const;

/**
* Claim mail attachments by category.
*
* @param Category Mail category, category 'All' represents all categories.
*/
void ClaimMailAttachmentByCategory(
EClientMailCategory Category,
TFunction<void(const FPgosResult& Ret, const FClientClaimMailAttachmentResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
TArray<FString> MailIds;
Mail->ClaimMailAttachmentById(MailIds, [](const FPgosResult& Ret, const FClientClaimMailAttachmentResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("ClaimMailAttachmentById Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("ClaimMailAttachmentById Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

void SomeUObjectClass::SomeFunction1()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
EClientMailCategory Category;
// specify mail category
Mail->ClaimMailAttachmentByCategory(Category, [](const FPgosResult& Ret, const FClientClaimMailAttachmentResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("ClaimMailAttachmentByCategory Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("ClaimMailAttachmentByCategory Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.5 Delete Mails

Players can delete mails by calling UPgosMailAPI::DeleteMailById/UPgosMailAPI::DeleteReadMailByCategory API. IT respectively means to delete mail by id and by category. It should be noted that UPgosMailAPI::DeleteReadMailByCategory is to delete the read mails by category, and UPgosMailAPI::DeleteMailById does not distinguish between read and unread.

In UPgosMailAPI::ClaimMailAttachmentByCategory API, EClientMailCategory::All represents all categorys.

Interface prototype:

/**
* Delete mail by mail ID.
*
* @param MailIds Mail IDs.
*/
void DeleteMailById(
const TArray<FString>& MailIds,
TFunction<void(const FPgosResult& Ret)> Callback) const;

/**
* Delete read mails by category.
*
* @param Category Mail category, category 'All' represents all categories.
*/
void DeleteReadMailByCategory(
EClientMailCategory Category,
TFunction<void(const FPgosResult& Ret)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
TArray<FString> MailIds;
Mail->DeleteMailById(MailIds, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DeleteMailById Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DeleteMailById Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

void SomeUObjectClass::SomeFunction()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{
EClientMailCategory Category;
Mail->DeleteReadMailByCategory(Category, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DeleteReadMailByCategory Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DeleteReadMailByCategory Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.6 Get the Count of Unread Mails

Players can get unread count per category, as well as total unread count by calling UPgosMailAPI::GetMailBadgeNum API.

Interface prototype:

/**
* Get the count of unread mails.
*/
void GetMailBadgeNum(TFunction<void(const FPgosResult& Ret, const FClientGetMailBadgeNumResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Mail = IPgosSDKCpp::Get().GetClientMailAPI();
if (Mail)
{

Mail->GetMailBadgeNum([](const FPgosResult& Ret, const FClientGetMailBadgeNumResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetMailBadgeNum Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetMailBadgeNum Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.7 Monitor Mail Events

Some events will be triggered when using the mail:

  • OnBadgeNumOfMail: Immediately triggered after login, and subsequently, it will also be triggered whenever there is a change in the unread count. The event results are saved in FClientBadgeNumOfMailEvt.
  • OnReceiveNewMail: This event is triggered in real-time when new mails arrive, it should be noted that mails received while offline will not trigger this event. The event results are saved in FClientReceiveNewMailEvt.
struct FClientBadgeNumOfMailEvt 
{
/** Total unread count. */
int32 total_badge_num = 0;
};

struct FClientReceiveNewMailEvt
{
/** New mail. */
FClientMailInfo mail;
};

7. Key Errors Handling

Error CodeRelevant APIHandling Suggestion
kBackendReceiverIDIllegal
kBackendReceiverEmpty
SendMailSender must specify at least one valid PlayerID to send mail successfully. Mail will throw an error if one of the PlayerIDs are invalid.
kBackendDefaultLanguageNotExistSendMailMail will respond mails to player in his local language defined in player info, when local language missing, mail will use the default language instead. So the sender must ensure the mail text map contains the default_language key.
kBackendIllegalCategoryEnumSendMailSender must specify a valid category enum when sending a mail. And MAIL_CATEGORY_ALL cannot be used since it has special meaning in batch processing interfaces.
kBackendMailTitleContainProfanityWords
kBackendMailContentContainProfanityWords
SendMailSender must ensure that the title and content of mail do not contain profane words.
kBackendGetMailCountBeyondLimitGetMailListGet mail count cannot be zero or exceed 100
kBackendGrantItemNumExceedClaimMailAttachmentById ClaimMailAttachmentByCategoryGrant items num exceeds limit
kBackendCheckIdempotentKeyFailedSendMailThere is an issue with idempotent key duplication.