migrate-to-sent

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Migrate to Sent

迁移至Sent

Every migration from a major CPaaS provider hits the same five translation problems. Work them in this order, because the first one silently doubles cost and is invisible in tests.
从主流CPaaS提供商迁移时都会遇到相同的五大转换问题,请按以下顺序处理,因为第一个问题会悄无声息地使成本翻倍,且在测试中无法被发现。

1. Ordered fallback becomes automatic routing

1. 有序回退转为自动路由

Incumbent platforms express cross-channel delivery through different caller-side arrays, failover objects, messaging-service features, or application-level priority configuration. Do not assume those shapes have a direct Sent request-field equivalent.
Sent's
channel
array is a broadcast list.
Porting an ordered array produces one message and one charge per recipient-channel pair, which passes tests and multiplies production spend. The correct translation is automatic routing — omit
channel
or send
["sent"]
— which lets the platform select a route and reroute across up to three channel-and-provider pairs on the same
message_id
. Details belong to
sent-routing-strategist
; the migration rule is simply: never port an ordered channel list.
现有平台通过不同的调用端数组、故障转移对象、消息服务功能或应用级优先级配置来实现跨渠道投递。不要假设这些结构在Sent中有对应的请求字段。
Sent的
channel
数组是一个广播列表
。移植有序数组会为每个接收者-渠道对生成一条消息并收取一次费用,这种情况在测试中不会报错,但会使生产环境的成本翻倍。正确的转换方式是使用自动路由——省略
channel
参数或传入
["sent"]
——这样平台会选择一条路由,并在同一个
message_id
下最多跨三个渠道-提供商对重新路由。详细信息可参考
sent-routing-strategist
;迁移规则很简单:切勿移植有序渠道列表

2. Status vocabularies do not line up

2. 状态术语无法直接匹配

Incumbent statuses map onto Sent's, but Sent adds two states that have no equivalent and that break naive retry logic.
Sent statusClosest incumbent analogueMigration note
QUEUED
Twilio
queued
, Sinch
QUEUED_ON_CHANNEL
Accepted, not sent
ROUTED
no analogueRoute chosen; fires again on reroute
SENT
Twilio
sent
, Sinch
MESSAGE_SUBMIT
Provider handoff only
DELIVERED
delivered
everywhere
The first proof of handset receipt
READ
Twilio
read
, Sinch
READ
WhatsApp and RCS only
FAILED
failed
,
undelivered
May still reroute; not necessarily final
FILTERED
Twilio error 21610 (opt-out)Policy gate. Never retry
BLOCKED
account-level errorsAccount precondition. Fix the account, then resend
SCHEDULED
no analogueQuiet-hours parking; resumes automatically
Two consequences for ported code. Handlers that treat every non-delivered terminal state as retryable will retry consent blocks, which is a compliance failure rather than a bug. And handlers keyed on numeric provider error codes — Twilio's
21610
is the classic — must be rewritten against Sent's string
error.code
families.
现有平台的状态可以映射到Sent的状态,但Sent新增了两个无对应项的状态,这会破坏简单的重试逻辑。
Sent状态最接近的现有平台对应状态迁移注意事项
QUEUED
Twilio
queued
、Sinch
QUEUED_ON_CHANNEL
已接受,但尚未发送
ROUTED
无对应项已选择路由;重新路由时会再次触发
SENT
Twilio
sent
、Sinch
MESSAGE_SUBMIT
仅完成提供商交接
DELIVERED
所有平台的
delivered
首次确认手机已接收
READ
Twilio
read
、Sinch
READ
仅适用于WhatsApp和RCS
FAILED
所有平台的
failed
undelivered
仍可能重新路由;不一定是最终状态
FILTERED
Twilio错误码21610(退订)策略关卡。切勿重试
BLOCKED
账户级错误账户前置条件。修复账户后重新发送
SCHEDULED
无对应项静默时段暂存;会自动恢复发送
这对移植代码有两个影响。将所有未投递的终端状态视为可重试的处理程序会重试合规拦截请求,这属于合规失败而非程序bug。而依赖数字提供商错误码(典型如Twilio的
21610
)的处理程序必须重写为基于Sent的字符串类型
error.code
族。

3. Webhook verification is a rewrite, not a port

3. Webhook验证需重写,而非移植

No two providers sign the same way, and no Sent SDK ships a verifier.
ProviderScheme
Twilio
X-Twilio-Signature
, base64 HMAC-SHA1 over the full URL plus sorted POST parameters
SinchHMAC-SHA256 over
body.nonce.timestamp
, four
x-sinch-webhook-signature*
headers, or OAuth 2.0
InfobipBasic, HMAC-SHA256 over the raw body, or OAuth on a notification profile; the header name is account-configured
VonageJWT in
Authorization: Bearer
, or a legacy
sig
parameter
MessageBird/Bird
messagebird-signature
, base64 HMAC-SHA256 over timestamp, URL, and a SHA-256 body hash
Sent
x-webhook-signature: v1,{base64}
, HMAC-SHA256 over
{x-webhook-id}.{x-webhook-timestamp}.{raw_body}
Sent's key is the signing secret with
whsec_
stripped and the remainder base64-decoded, compared in constant time, with timestamps outside 300 seconds rejected. Because Sent provides no per-event id, dedupe keys must be derived from payload semantics. Build the receiver with
sent-webhook-engineer
rather than adapting the incumbent's verifier.
没有两个提供商的签名方式相同,且Sent SDK不提供验证器。
提供商签名方案
Twilio
X-Twilio-Signature
,基于完整URL加排序后的POST参数的base64 HMAC-SHA1签名
Sinch基于
body.nonce.timestamp
的HMAC-SHA256签名、四个
x-sinch-webhook-signature*
头部,或OAuth 2.0
InfobipBasic认证、基于原始请求体的HMAC-SHA256签名,或通知配置文件上的OAuth;头部名称由账户配置
Vonage
Authorization: Bearer
中的JWT,或遗留的
sig
参数
MessageBird/Bird
messagebird-signature
,基于时间戳、URL和请求体SHA-256哈希值的base64 HMAC-SHA256签名
Sent
x-webhook-signature: v1,{base64}
,基于
{x-webhook-id}.{x-webhook-timestamp}.{raw_body}
的HMAC-SHA256签名
Sent的密钥是去除
whsec_
前缀后剩余部分经base64解码得到的签名密钥,需通过常量时间比较验证,且会拒绝超出300秒的时间戳。由于Sent不提供每个事件的ID,去重密钥必须从负载语义中推导。请使用
sent-webhook-engineer
构建接收端,而非修改现有平台的验证器。

4. Opt-out stores must be reconciled, not migrated by copy

4. 退订存储需对账,而非直接复制迁移

Every provider keeps its own suppression list — Twilio Advanced Opt-Out, Infobip Blocklist, Sinch OPT_IN/OPT_OUT events. Sent enforces consent at the platform level before events reach the application, stores it as
opt_out
on the contact, and applies it channel-agnostically: a
STOP
on SMS suppresses WhatsApp and RCS too.
Reconciliation rules: export the incumbent's suppression list before cutover, treat any opt-out on any incumbent channel as a global Sent opt-out, and never clear
opt_out
to "clean up" migrated data. Sent's ten default keywords are
STOP
,
CANCEL
,
UNSUBSCRIBE
,
QUIT
,
END
,
START
,
UNSTOP
,
SUBSCRIBE
,
HELP
,
INFO
, matched only when the entire trimmed body equals the keyword — so incumbent-specific keywords need custom keyword entries. Rewrite any incumbent keyword matcher as an exact local consent mirror and audit mechanism; the matcher must not write consent to Sent again. Consent semantics belong to
sent-two-way-messaging
.
每个提供商都有自己的抑制列表——如Twilio Advanced Opt-Out、Infobip Blocklist、Sinch OPT_IN/OPT_OUT事件。Sent在平台层面强制执行合规同意,在事件到达应用前就进行处理,将其存储为联系人的
opt_out
字段,并且跨渠道统一应用:短信渠道的
STOP
指令会同时屏蔽WhatsApp和RCS渠道。
对账规则:切换前导出现有平台的抑制列表,将现有平台任意渠道的退订视为Sent全局退订,切勿为“清理”迁移数据而清除
opt_out
字段。Sent的十个默认关键词为
STOP
CANCEL
UNSUBSCRIBE
QUIT
END
START
UNSTOP
SUBSCRIBE
HELP
INFO
,仅当修剪后的请求体完全匹配关键词时才会触发——因此现有平台的特定关键词需要添加自定义关键词条目。将现有平台的关键词匹配器重写为精确的本地合规镜像和审计机制;该匹配器不得再次向Sent写入合规同意信息。合规同意语义相关内容可参考
sent-two-way-messaging

5. Templates and tenancy are re-registered, not transferred

5. 模板和租户需重新注册,而非转移

WhatsApp templates live with the WABA, so the migration question is whether the WABA moves. Positional placeholders (
{{1}}
,
{{2}}
) become named parameters in Sent, which means every call site that passed an ordered array must pass a named map. Approval is asynchronous and arrives as a
templates
webhook event, so build the template inventory before cutover rather than during it.
Tenancy maps as follows, with the boundary decision owned by
sender-profile-architect
and the API work by
sent-profile-provisioning
:
Incumbent constructSent equivalent
Twilio subaccountSender Profile
Twilio Messaging Servicerouting plus profile configuration, not a caller-side pool
Infobip Application or EntitySender Profile
Sinch Conversation API appSender Profile
Provider API credential per tenantProfile-scoped API key, or organization key with
x-profile-id
WhatsApp模板归属于WABA(WhatsApp Business Account),因此迁移的核心问题是是否迁移WABA。位置占位符(
{{1}}
{{2}}
)在Sent中变为命名参数,这意味着所有传入有序数组的调用点都必须传入命名映射。模板审批是异步的,会以
templates
Webhook事件形式通知,因此请在切换前完成模板清单的构建,而非在切换过程中进行。
租户映射如下,边界决策由
sender-profile-architect
负责,API操作由
sent-profile-provisioning
负责:
现有平台结构Sent对应项
Twilio子账户Sender Profile(发送者配置文件)
Twilio消息服务路由加配置文件配置,而非调用端池
Infobip应用或实体Sender Profile
Sinch对话API应用Sender Profile
每个租户的提供商API凭证配置文件范围的API密钥,或带有
x-profile-id
的组织密钥

Migration sequence

迁移流程

  1. Inventory every send call site, webhook handler, status branch, template, suppression list, and credential. Use
    scripts/inventory_scan.py
    to find them mechanically.
  2. Map each item using references/provider-mapping.md, flagging ordered-fallback arrays and numeric error codes as required rewrites.
  3. Stand up Sent in parallel: credentials, one webhook per environment, verified receiver, templates re-registered and approved.
  4. Prove equivalence in sandbox with
    "sandbox": true
    , then with a small live cohort confirmed to
    DELIVERED
    .
  5. Dual-run with a traffic split, comparing delivery rates, latency, and cost per message on the same message classes.
  6. Cut over by message class — lowest-risk transactional first, marketing last — keeping the incumbent receiver live.
  7. Decommission only after a full billing cycle of clean data, then revoke incumbent credentials.
Sequencing detail, verification gates, and rollback triggers are in references/cutover-playbook.md.
  1. 盘点所有发送调用点、Webhook处理程序、状态分支、模板、抑制列表和凭证。使用
    scripts/inventory_scan.py
    进行自动盘点。
  2. 映射每个条目,参考[references/provider-mapping.md],标记有序回退数组和数字错误码作为必须重写的内容。
  3. 并行部署Sent:配置凭证、为每个环境设置一个Webhook、验证接收端、重新注册并审批模板。
  4. 在沙箱环境验证等效性,设置
    "sandbox": true
    ,然后在小批量真实用户群体中确认消息已
    DELIVERED
  5. 双运行并分流流量,对比相同消息类型的投递率、延迟和单条消息成本。
  6. 按消息类型切换:先切换风险最低的交易类消息,最后切换营销类消息,同时保持现有平台的接收端处于活跃状态。
  7. 停用现有平台:仅在完成一个完整计费周期且数据无异常后,再撤销现有平台的凭证。
流程细节、验证关卡和回滚触发条件可参考[references/cutover-playbook.md]。

Mistakes that survive testing

测试无法发现的错误

  • Porting an ordered channel array. Doubles cost, never errors.
  • Treating
    FILTERED
    as retryable. Compliance exposure.
  • Reusing the incumbent's signature verifier. Every delivery returns 401.
  • Assuming
    202
    means delivered. Sent acknowledges acceptance only.
  • Keeping positional template placeholders. Parameters silently mismatch.
  • Retrying on
    401
    . Ten consecutive auth failures lock the credential with escalating lockout.
  • Omitting
    Idempotency-Key
    during dual-run. A timeout retry sends twice.
  • Sending
    x-profile-id
    with a profile-scoped key. Returns
    403
    .
  • Copying an incumbent's
    Authorization: Bearer
    pattern. Sent authenticates with
    x-api-key
    .
  • 移植有序渠道数组:成本翻倍,且不会报错。
  • FILTERED
    视为可重试状态:引发合规风险。
  • 复用现有平台的签名验证器:所有投递请求都会返回401。
  • 认为
    202
    表示已投递:Sent仅确认请求已被接受。
  • 保留位置式模板占位符:参数会静默不匹配。
  • 401
    时重试:连续十次认证失败会锁定凭证,且锁定时长逐渐增加。
  • 双运行期间省略
    Idempotency-Key
    :超时重试会导致重复发送。
  • 使用配置文件范围的密钥时传入
    x-profile-id
    :返回
    403
  • 复用现有平台的
    Authorization: Bearer
    模式:Sent使用
    x-api-key
    进行认证。

Boundaries

职责边界

This skill owns provider mapping and line-by-line migration planning. Hand the resulting Sent client and resilience work to
sent-integration-starter
, channel semantics to
sent-routing-strategist
, receiver construction to
sent-webhook-engineer
, WhatsApp onboarding to
waba-embedded-signup
, and US campaign registration to
sms-10dlc-registration
.
本技能负责提供商映射和逐行迁移规划。生成的Sent客户端和弹性相关工作交由
sent-integration-starter
处理,渠道语义交由
sent-routing-strategist
处理,接收端构建交由
sent-webhook-engineer
处理,WhatsApp入驻交由
waba-embedded-signup
处理,美国短信Campaign注册交由
sms-10dlc-registration
处理。