构建电子邮件归档系统:挑战与解决方案 - 第1部分

杰夫·戈德斯坦

2019年2月4日

电子邮件

1 min read

构建电子邮件归档系统:挑战与解决方案 - 第1部分

关键要点

    • Email archiving 是日益重要的,特别是在监管、合规和审计环境中。

    • SparkPost 不存储邮件正文,但其 Archive feature 允许发送者接收与跟踪链接和内容相吻合的重复消息。

    • 邮件正文可以存储在 Amazon S3 中,而消息事件元数据可以存储在 MySQL 中以供查询和交叉引用。

    • SparkPost 消息事件提供丰富的活动日志(退信、投递、点击、打开、退订、投诉等)。

    • 只有通过 SMTP 发送邮件时才会生成档案副本。

    • 原始邮件、档案邮件、CC 和 BCC 邮件的消息事件共享一个 transmission_id

    • 入站邮件中继可以摄取档案消息,但不包括 transmission_id ,这造成了数据链接的挑战。

    • 在消息正文中嵌入 hidden unique identifier (UID) 可以弥补这一差距,将入站内容与出站日志联系起来。

    • 结合档案邮件和消息事件可以建立一个可搜索的、可审核的档案系统。

    • 长期项目包括在 S3 中存储档案邮件和在 MySQL 中记录事件数据的代码发布。

    • 最终应用程序将允许轻松搜索、查看和协调邮件内容与所有相关事件历史。

    • 非常适合那些需要全面了解每封发送邮件的合规性重工业。

Q&A 精华

  • 为什么构建自己的电子邮件归档系统?

    受监管行业通常需要长期存储电子邮件正文及所有相关的事件日志。SparkPost 不存储消息正文,因此建立自定义系统以确保合规性、审计和可见性。

  • 您如何获取原始发送电子邮件的精确副本?

    SparkPost的Archive feature会将每封外发电子邮件的副本发送到指定的存档地址,保留所有编码链接和跟踪行为。

  • 为什么您无法在发送前捕获电子邮件正文?

    预发送捕获不包括SparkPost的修改(打开跟踪、点击跟踪、链接编码)。使用Archive副本确保您的保存版本与收件人收到的内容完全匹配。

  • SparkPost会自动归档电子邮件吗?

    不。SparkPost 存储消息正文。必须在 SMTP 注入期间通过指定存档地址来请求存档副本。

  • 在这个归档系统中,哪些内容存储在何处?

    • Email body → Amazon S3

    • Message event logs → MySQL
      这种分离支持快速搜索、结构化查询和低成本对象存储。

  • SparkPost 会保留事件数据多长时间?

    SparkPost 存储消息事件10天。之后,数据必须通过 webhook 获取或查询并存储在其他地方。

  • 有哪些消息事件可用?

    SparkPost 目前公开 14 个事件,包括 deliveries、bounces、clicks、opens、rejections、policy issues、spam complaints、unsubscribes, 和更多。

  • 什么标识符将所有事件联系在一起?

    所有外发邮件(原件、存档、CC、BCC)共享相同的transmission_id。原件和存档邮件也共享相同的message_id

  • 为何入站处理是一个挑战?

    SparkPost 的入站电子邮件中继将入站电子邮件转换为 JSON,但此 JSON 不包括 transmission_id。没有额外的数据,入站副本无法链接到其出站日志历史。

  • 如何将入站存档电子邮件连接到出站消息事件?

    在电子邮件正文中嵌入一个隐藏的unique identifier (UID),并在元数据中传递相同的UID。这个UID成为入站和出站记录之间的共享引用。

  • 入站电子邮件中继如何帮助自动化存档?

    它接收发送到您存档域的档案邮件,将它们解析为结构化的JSON,并通过webhook发布到您的应用程序——允许自动提取和存储。

  • 这个项目的长期愿景是什么?

    一个完整的应用程序:

    • 在 S3 中存储归档邮件

    • 在 MySQL 中存储所有事件日志

    • 允许用户搜索邮件

    • 在一个统一界面中显示原始邮件及每个相关事件

大约一年前,我写了一篇博客关于如何检索电子邮件的副本以进行存档和查看,但我没有讨论实际存储电子邮件或相关数据,最近我写了一篇博客关于存储所有事件数据(例如,邮件发送时间、打开、点击、弹回、退订等)以进行审计目的,但选择不创建任何支持代码。

随着在监管环境中电子邮件使用的增加,我决定是时候开始一个新项目,提供存储电子邮件正文及其所有关联数据的代码示例。未来一年,我将继续构建该项目,目的是创建一个存档邮件和所有日志信息的存储和查看应用程序,所有这些信息均由SparkPost生成。SparkPost 并没有一个存档电子邮件正文的系统,但它确实使构建一个存档平台相对简单。

在这个博客系列中,我将描述将电子邮件正文存储到 S3(亚马逊简单存储服务)和所有相关日志数据存储到 MySQL 中便于交叉引用的过程。对于需要强大数据库备份策略的生产型存档系统,请考虑实施一个全面的PostgreSQL 备份和还原过程以确保存档数据得到妥善保护。最终,这是构建一个允许轻松搜索存档邮件的应用程序的起点,然后显示这些邮件及事件(日志)数据。此项目的代码可以在以下 GitHub 仓库中找到:GitHub 上的 PHPArchivePlatform

本博客系列的第一篇文章将描述这个挑战并为解决方案制定架构。其余博客将详细描述解决方案的各个部分,并包含代码示例。

我过程的第一步是弄清楚如何获得发送给原始收件人的电子邮件的副本。为了获得电子邮件正文的副本,您需要:

  1. 在发送电子邮件之前捕获电子邮件正文

  2. 让邮件服务器存储一个副本

  3. 让邮件服务器为您创建一个存储副本

如果邮件服务器正在添加例如链接跟踪或打开跟踪这样的项目,您不能使用#1,因为它不会反映打开/点击跟踪的更改。

这意味着服务器必须存储电子邮件,或者以某种方式为您提供电子邮件的副本以供存储。由于 SparkPost 没有用于存储电子邮件正文的机制,但确实有一种方法可以创建电子邮件的副本,我们将让 SparkPost 发送电子邮件的副本给我们,以便我们存储在 S3 中。

这通过使用 SparkPost 的存档功能来完成。SparkPost 的存档功能允许发件人告诉 SparkPost 将电子邮件的副本发送给一个或多个电子邮件地址,并使用与原件相同的跟踪和打开链接。SparkPost 文档定义其存档功能如下:

存档列表中的收件人将收到发送给 RCPT TO 地址的邮件的精确副本。特别是,任何为 RCPT TO 收件人配置的编码链接将在存档邮件中是相同的。

与 RCPT TO 电子邮件唯一不同的是,一些标头会有所不同,因为存档电子邮件的目标地址是不同的,但电子邮件的正文将是精确的副本!

如果您想要更深入的解释,这里有一个链接到SparkPost 文档,关于创建电子邮件的复制(或存档)副本。

题外话,SparkPost 实际上允许您将邮件发送到 cc、bcc 和存档电子邮件地址。对于此解决方案,我们专注于存档地址。

* 注意 * 仅在通过 SMTP 注入电子邮件到 SparkPost 时才可创建存档邮件!

现在我们知道如何获取原始邮件的副本了,我们需要查看生成的日志数据以及该数据中的一些细微变化。SparkPost 跟踪其服务器上发生的所有事件,并将这些信息以消息事件的形式提供给您。这些事件存储在 SparkPost 上 10 天,并可以通过称为消息事件的 RESTful API 从服务器中提取,或者您可以让 SparkPost 将这些事件推送到您希望的任何数量的收集应用程序中。通过 webhooks 机制实时完成推送。

目前,可能发生在电子邮件上的不同事件共有 14 种。以下是当前事件的列表:

  • Bounce

  • ClickDelay

  • Delivery

  • Generation Failure

  • Generation Rejection

  • Initial Open

  • InjectionLink Unsubscribe

  • List Unsubscribe

  • Open

  • Out of Band

  • Policy Rejection Spam Complaint


* 请参考此链接作为最新的参考指南,其中包括有关每个事件的描述以及每个事件共享的数据。

每个事件都有与事件类型匹配的众多字段 一些字段例如 transmission_id 在每个事件中都可找到,但其他字段可能更具事件特别性;例如,仅打开和点击事件有地理标签信息。

此项目中一个非常重要的消息事件条目是 transmission_id。原始邮件、存档邮件以及任何ccbcc地址的所有消息事件条目将共享相同的 transmission_id。

还有一个叫做 message_id 的通用条目,原始邮件和存档邮件每条记录都有相同的 id。任何 cc 或 bcc 地址将为 message_id 条目各自拥有自己的 id。

到目前为止,这听起来很棒而且相当简单,但现在是挑战的部分。请记住,为了获取存档邮件,我们让 SparkPost 发送原始邮件的副本到另一个您可以访问的收件箱中。但是为了自动化该解决方案并存储电子邮件正文,我将使用 SparkPost 的另一个功能,称为Incoming Mail Relaying。这会接收所有发送到特定域的邮件并处理它们。通过处理,解析电子邮件并创建 JSON 结构,然后通过 webhook 将其传递给应用程序。请参见附录 A 了解示例 JSON。

如果您仔细观察,您会注意到传入中继的 JSON 结构缺少一个非常重要的字段;transmission_id。虽然所有的外发电子邮件都有 transmission_id ,且所有原始电子邮件、存档、cc 和 bcc 地址的数据都被同一个条目绑定在一起;但 SparkPost 无法知道通过传入过程捕获的邮件与任何外发邮件相关联。传入过程只知道邮件被发送到特定域,并要解析邮件。就是这样。无论是客户的回复还是 SparkPost 发送的存档邮件,它都会以相同方式处理发送到该域的任何邮件。

那么诀窍是;如何将外发数据与刚刚抓取存档电子邮件的传入过程粘合在一起?我决定在电子邮件正文中隐藏一个唯一 id。如何做到这一点由您决定,但我只是创建了一个打开隐藏标记的输入框。

<input name="ArchiveCode" type="hidden" value="<<UID>>">

我还将该字段添加到 X-MSYS-API 头部的元数据块中,该块在注入到 SparkPost 时被传送。这个隐藏的 UID 将成为整个过程的粘合剂,是该项目的主要组成部分,并将在接下来的博客文章中深入讨论。

现在我们有了将整个项目粘合在一起的 UID 并理解了为什么它是必要的,我可以开始构建整个项目和相应的博客文章的蓝图。

  1. 捕获并存储存档电子邮件以及用于搜索/索引的数据库条目

  2. 捕获所有消息事件数据

  3. 创建一个应用程序以查看电子邮件和所有相应的数据

以下是项目的简单图表:

build an email archiving system - diagram


第一批代码将涵盖存档过程,并将电子邮件存储到 S3 中,而第二批代码将涵盖将所有消息事件的日志数据存储到 MySQL 中。第一和第二批代码以及博客条目预计将在 2019 年初发布。如果您有任何问题或建议,请随时提出。

发送愉快。
– Jeff


附录 A:

JSON file example - email archiving system

大约一年前,我写了一篇博客关于如何检索电子邮件的副本以进行存档和查看,但我没有讨论实际存储电子邮件或相关数据,最近我写了一篇博客关于存储所有事件数据(例如,邮件发送时间、打开、点击、弹回、退订等)以进行审计目的,但选择不创建任何支持代码。

随着在监管环境中电子邮件使用的增加,我决定是时候开始一个新项目,提供存储电子邮件正文及其所有关联数据的代码示例。未来一年,我将继续构建该项目,目的是创建一个存档邮件和所有日志信息的存储和查看应用程序,所有这些信息均由SparkPost生成。SparkPost 并没有一个存档电子邮件正文的系统,但它确实使构建一个存档平台相对简单。

在这个博客系列中,我将描述将电子邮件正文存储到 S3(亚马逊简单存储服务)和所有相关日志数据存储到 MySQL 中便于交叉引用的过程。对于需要强大数据库备份策略的生产型存档系统,请考虑实施一个全面的PostgreSQL 备份和还原过程以确保存档数据得到妥善保护。最终,这是构建一个允许轻松搜索存档邮件的应用程序的起点,然后显示这些邮件及事件(日志)数据。此项目的代码可以在以下 GitHub 仓库中找到:GitHub 上的 PHPArchivePlatform

本博客系列的第一篇文章将描述这个挑战并为解决方案制定架构。其余博客将详细描述解决方案的各个部分,并包含代码示例。

我过程的第一步是弄清楚如何获得发送给原始收件人的电子邮件的副本。为了获得电子邮件正文的副本,您需要:

  1. 在发送电子邮件之前捕获电子邮件正文

  2. 让邮件服务器存储一个副本

  3. 让邮件服务器为您创建一个存储副本

如果邮件服务器正在添加例如链接跟踪或打开跟踪这样的项目,您不能使用#1,因为它不会反映打开/点击跟踪的更改。

这意味着服务器必须存储电子邮件,或者以某种方式为您提供电子邮件的副本以供存储。由于 SparkPost 没有用于存储电子邮件正文的机制,但确实有一种方法可以创建电子邮件的副本,我们将让 SparkPost 发送电子邮件的副本给我们,以便我们存储在 S3 中。

这通过使用 SparkPost 的存档功能来完成。SparkPost 的存档功能允许发件人告诉 SparkPost 将电子邮件的副本发送给一个或多个电子邮件地址,并使用与原件相同的跟踪和打开链接。SparkPost 文档定义其存档功能如下:

存档列表中的收件人将收到发送给 RCPT TO 地址的邮件的精确副本。特别是,任何为 RCPT TO 收件人配置的编码链接将在存档邮件中是相同的。

与 RCPT TO 电子邮件唯一不同的是,一些标头会有所不同,因为存档电子邮件的目标地址是不同的,但电子邮件的正文将是精确的副本!

如果您想要更深入的解释,这里有一个链接到SparkPost 文档,关于创建电子邮件的复制(或存档)副本。

题外话,SparkPost 实际上允许您将邮件发送到 cc、bcc 和存档电子邮件地址。对于此解决方案,我们专注于存档地址。

* 注意 * 仅在通过 SMTP 注入电子邮件到 SparkPost 时才可创建存档邮件!

现在我们知道如何获取原始邮件的副本了,我们需要查看生成的日志数据以及该数据中的一些细微变化。SparkPost 跟踪其服务器上发生的所有事件,并将这些信息以消息事件的形式提供给您。这些事件存储在 SparkPost 上 10 天,并可以通过称为消息事件的 RESTful API 从服务器中提取,或者您可以让 SparkPost 将这些事件推送到您希望的任何数量的收集应用程序中。通过 webhooks 机制实时完成推送。

目前,可能发生在电子邮件上的不同事件共有 14 种。以下是当前事件的列表:

  • Bounce

  • ClickDelay

  • Delivery

  • Generation Failure

  • Generation Rejection

  • Initial Open

  • InjectionLink Unsubscribe

  • List Unsubscribe

  • Open

  • Out of Band

  • Policy Rejection Spam Complaint


* 请参考此链接作为最新的参考指南,其中包括有关每个事件的描述以及每个事件共享的数据。

每个事件都有与事件类型匹配的众多字段 一些字段例如 transmission_id 在每个事件中都可找到,但其他字段可能更具事件特别性;例如,仅打开和点击事件有地理标签信息。

此项目中一个非常重要的消息事件条目是 transmission_id。原始邮件、存档邮件以及任何ccbcc地址的所有消息事件条目将共享相同的 transmission_id。

还有一个叫做 message_id 的通用条目,原始邮件和存档邮件每条记录都有相同的 id。任何 cc 或 bcc 地址将为 message_id 条目各自拥有自己的 id。

到目前为止,这听起来很棒而且相当简单,但现在是挑战的部分。请记住,为了获取存档邮件,我们让 SparkPost 发送原始邮件的副本到另一个您可以访问的收件箱中。但是为了自动化该解决方案并存储电子邮件正文,我将使用 SparkPost 的另一个功能,称为Incoming Mail Relaying。这会接收所有发送到特定域的邮件并处理它们。通过处理,解析电子邮件并创建 JSON 结构,然后通过 webhook 将其传递给应用程序。请参见附录 A 了解示例 JSON。

如果您仔细观察,您会注意到传入中继的 JSON 结构缺少一个非常重要的字段;transmission_id。虽然所有的外发电子邮件都有 transmission_id ,且所有原始电子邮件、存档、cc 和 bcc 地址的数据都被同一个条目绑定在一起;但 SparkPost 无法知道通过传入过程捕获的邮件与任何外发邮件相关联。传入过程只知道邮件被发送到特定域,并要解析邮件。就是这样。无论是客户的回复还是 SparkPost 发送的存档邮件,它都会以相同方式处理发送到该域的任何邮件。

那么诀窍是;如何将外发数据与刚刚抓取存档电子邮件的传入过程粘合在一起?我决定在电子邮件正文中隐藏一个唯一 id。如何做到这一点由您决定,但我只是创建了一个打开隐藏标记的输入框。

<input name="ArchiveCode" type="hidden" value="<<UID>>">

我还将该字段添加到 X-MSYS-API 头部的元数据块中,该块在注入到 SparkPost 时被传送。这个隐藏的 UID 将成为整个过程的粘合剂,是该项目的主要组成部分,并将在接下来的博客文章中深入讨论。

现在我们有了将整个项目粘合在一起的 UID 并理解了为什么它是必要的,我可以开始构建整个项目和相应的博客文章的蓝图。

  1. 捕获并存储存档电子邮件以及用于搜索/索引的数据库条目

  2. 捕获所有消息事件数据

  3. 创建一个应用程序以查看电子邮件和所有相应的数据

以下是项目的简单图表:

build an email archiving system - diagram


第一批代码将涵盖存档过程,并将电子邮件存储到 S3 中,而第二批代码将涵盖将所有消息事件的日志数据存储到 MySQL 中。第一和第二批代码以及博客条目预计将在 2019 年初发布。如果您有任何问题或建议,请随时提出。

发送愉快。
– Jeff


附录 A:

JSON file example - email archiving system

大约一年前,我写了一篇博客关于如何检索电子邮件的副本以进行存档和查看,但我没有讨论实际存储电子邮件或相关数据,最近我写了一篇博客关于存储所有事件数据(例如,邮件发送时间、打开、点击、弹回、退订等)以进行审计目的,但选择不创建任何支持代码。

随着在监管环境中电子邮件使用的增加,我决定是时候开始一个新项目,提供存储电子邮件正文及其所有关联数据的代码示例。未来一年,我将继续构建该项目,目的是创建一个存档邮件和所有日志信息的存储和查看应用程序,所有这些信息均由SparkPost生成。SparkPost 并没有一个存档电子邮件正文的系统,但它确实使构建一个存档平台相对简单。

在这个博客系列中,我将描述将电子邮件正文存储到 S3(亚马逊简单存储服务)和所有相关日志数据存储到 MySQL 中便于交叉引用的过程。对于需要强大数据库备份策略的生产型存档系统,请考虑实施一个全面的PostgreSQL 备份和还原过程以确保存档数据得到妥善保护。最终,这是构建一个允许轻松搜索存档邮件的应用程序的起点,然后显示这些邮件及事件(日志)数据。此项目的代码可以在以下 GitHub 仓库中找到:GitHub 上的 PHPArchivePlatform

本博客系列的第一篇文章将描述这个挑战并为解决方案制定架构。其余博客将详细描述解决方案的各个部分,并包含代码示例。

我过程的第一步是弄清楚如何获得发送给原始收件人的电子邮件的副本。为了获得电子邮件正文的副本,您需要:

  1. 在发送电子邮件之前捕获电子邮件正文

  2. 让邮件服务器存储一个副本

  3. 让邮件服务器为您创建一个存储副本

如果邮件服务器正在添加例如链接跟踪或打开跟踪这样的项目,您不能使用#1,因为它不会反映打开/点击跟踪的更改。

这意味着服务器必须存储电子邮件,或者以某种方式为您提供电子邮件的副本以供存储。由于 SparkPost 没有用于存储电子邮件正文的机制,但确实有一种方法可以创建电子邮件的副本,我们将让 SparkPost 发送电子邮件的副本给我们,以便我们存储在 S3 中。

这通过使用 SparkPost 的存档功能来完成。SparkPost 的存档功能允许发件人告诉 SparkPost 将电子邮件的副本发送给一个或多个电子邮件地址,并使用与原件相同的跟踪和打开链接。SparkPost 文档定义其存档功能如下:

存档列表中的收件人将收到发送给 RCPT TO 地址的邮件的精确副本。特别是,任何为 RCPT TO 收件人配置的编码链接将在存档邮件中是相同的。

与 RCPT TO 电子邮件唯一不同的是,一些标头会有所不同,因为存档电子邮件的目标地址是不同的,但电子邮件的正文将是精确的副本!

如果您想要更深入的解释,这里有一个链接到SparkPost 文档,关于创建电子邮件的复制(或存档)副本。

题外话,SparkPost 实际上允许您将邮件发送到 cc、bcc 和存档电子邮件地址。对于此解决方案,我们专注于存档地址。

* 注意 * 仅在通过 SMTP 注入电子邮件到 SparkPost 时才可创建存档邮件!

现在我们知道如何获取原始邮件的副本了,我们需要查看生成的日志数据以及该数据中的一些细微变化。SparkPost 跟踪其服务器上发生的所有事件,并将这些信息以消息事件的形式提供给您。这些事件存储在 SparkPost 上 10 天,并可以通过称为消息事件的 RESTful API 从服务器中提取,或者您可以让 SparkPost 将这些事件推送到您希望的任何数量的收集应用程序中。通过 webhooks 机制实时完成推送。

目前,可能发生在电子邮件上的不同事件共有 14 种。以下是当前事件的列表:

  • Bounce

  • ClickDelay

  • Delivery

  • Generation Failure

  • Generation Rejection

  • Initial Open

  • InjectionLink Unsubscribe

  • List Unsubscribe

  • Open

  • Out of Band

  • Policy Rejection Spam Complaint


* 请参考此链接作为最新的参考指南,其中包括有关每个事件的描述以及每个事件共享的数据。

每个事件都有与事件类型匹配的众多字段 一些字段例如 transmission_id 在每个事件中都可找到,但其他字段可能更具事件特别性;例如,仅打开和点击事件有地理标签信息。

此项目中一个非常重要的消息事件条目是 transmission_id。原始邮件、存档邮件以及任何ccbcc地址的所有消息事件条目将共享相同的 transmission_id。

还有一个叫做 message_id 的通用条目,原始邮件和存档邮件每条记录都有相同的 id。任何 cc 或 bcc 地址将为 message_id 条目各自拥有自己的 id。

到目前为止,这听起来很棒而且相当简单,但现在是挑战的部分。请记住,为了获取存档邮件,我们让 SparkPost 发送原始邮件的副本到另一个您可以访问的收件箱中。但是为了自动化该解决方案并存储电子邮件正文,我将使用 SparkPost 的另一个功能,称为Incoming Mail Relaying。这会接收所有发送到特定域的邮件并处理它们。通过处理,解析电子邮件并创建 JSON 结构,然后通过 webhook 将其传递给应用程序。请参见附录 A 了解示例 JSON。

如果您仔细观察,您会注意到传入中继的 JSON 结构缺少一个非常重要的字段;transmission_id。虽然所有的外发电子邮件都有 transmission_id ,且所有原始电子邮件、存档、cc 和 bcc 地址的数据都被同一个条目绑定在一起;但 SparkPost 无法知道通过传入过程捕获的邮件与任何外发邮件相关联。传入过程只知道邮件被发送到特定域,并要解析邮件。就是这样。无论是客户的回复还是 SparkPost 发送的存档邮件,它都会以相同方式处理发送到该域的任何邮件。

那么诀窍是;如何将外发数据与刚刚抓取存档电子邮件的传入过程粘合在一起?我决定在电子邮件正文中隐藏一个唯一 id。如何做到这一点由您决定,但我只是创建了一个打开隐藏标记的输入框。

<input name="ArchiveCode" type="hidden" value="<<UID>>">

我还将该字段添加到 X-MSYS-API 头部的元数据块中,该块在注入到 SparkPost 时被传送。这个隐藏的 UID 将成为整个过程的粘合剂,是该项目的主要组成部分,并将在接下来的博客文章中深入讨论。

现在我们有了将整个项目粘合在一起的 UID 并理解了为什么它是必要的,我可以开始构建整个项目和相应的博客文章的蓝图。

  1. 捕获并存储存档电子邮件以及用于搜索/索引的数据库条目

  2. 捕获所有消息事件数据

  3. 创建一个应用程序以查看电子邮件和所有相应的数据

以下是项目的简单图表:

build an email archiving system - diagram


第一批代码将涵盖存档过程,并将电子邮件存储到 S3 中,而第二批代码将涵盖将所有消息事件的日志数据存储到 MySQL 中。第一和第二批代码以及博客条目预计将在 2019 年初发布。如果您有任何问题或建议,请随时提出。

发送愉快。
– Jeff


附录 A:

JSON file example - email archiving system

其他新闻

阅读更多来自此类别的内容

A person is standing at a desk while typing on a laptop.

完整的AI原生平台,可与您的业务一起扩展。

© 2025 Bird

A person is standing at a desk while typing on a laptop.

完整的AI原生平台,可与您的业务一起扩展。

© 2025 Bird