mcpField
C
clerk-sms-logs

clerk-sms-logs

github.com/clerk开发8992 次安装今天更新
代码仓库

关于 clerk-sms-logs

clerk-sms-logs 是用于排查 Clerk 所发送短信投递过程的 Skill。开发者可借助 Application Logs 区分 Clerk 拦截、服务商拒绝、运营商投递问题及尚未确认的结果。

主要功能

  1. 01说明五种短信生命周期事件
  2. 02通过 Dashboard 或 Backend API 查询日志
  3. 03按时间、号码、用户和 trace 筛选
  4. 04提供失败诊断步骤
  5. 05说明事件范围与遥测限制

适用场景

  1. 01排查未收到验证码
  2. 02区分发送前拦截与运营商失败
  3. 03查看某个号码的短信时间线

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/clerk/skills --skill clerk-sms-logs

安装后在 Claude Code 中输入/clerk-sms-logs确认已加载。

附带文件references/events.md

详细介绍

短信日志

回答一个问题:这条由 Clerk 发送的短信发生了什么? sms.* 应用日志会记录 Clerk 代表实例发送的每条短信的送达生命周期——电话验证代码、OTP、密码重置代码——让你无需猜测,就能判断是注册卡住、运营商拒收,还是被 Clerk 拦截。

可以通过两种方式查看:在 Clerk Dashboard → Application Logs 中筛选 sms.* 事件类型,或通过 Backend API (GET /v1/logs)以编程方式查询——数据相同,且可通过脚本重复调查。 这些是关于终端用户活动的送达遥测数据,不是控制面板操作。

查询日志

Backend API 的 logs 端点需要密钥(sk_*)。最简单的调用方式是使用 Clerk CLI,它会自动为你注入身份验证信息:

# SMS lifecycle events in the last 24h, newest first
SINCE=$(( ($(date +%s) - 86400) * 1000 ))
clerk api "/logs?type=sms.*&event_time_after=${SINCE}&limit=20"

# Just failures, same window
clerk api "/logs?type=sms.failed&event_time_after=${SINCE}&limit=20"

# One phone number's SMS history (exact-match payload filter)
# channel=sms keeps WhatsApp off the timeline — see the note below
clerk api "/logs?type=sms.*&payload_filter[phone_number]=%2B14155550100&payload_filter[channel]=sms&event_time_after=${SINCE}"

# Why a number's sends failed — concrete type unlocks reason/raw_error
clerk api "/logs?type=sms.failed&payload_filter[phone_number]=%2B14155550100&event_time_after=${SINCE}&payload_fields=phone_number,reason,raw_error,rejected_before_send"

# Every event for one message, by its trace
clerk api "/logs?type=sms.*&trace_id=<trace_id>&event_time_after=${SINCE}"

始终限定时间范围。 这些日志量很大,因此应从较窄的 event_time_after 开始(如果大致知道短信发送时间,也可设置 event_time_before);只有在没有结果时才扩大范围——不要扫描整个保留窗口来查找一条短信。将 limit 保持在适中范围(10–50),并使用返回的游标翻页,而不是调高它。该端点会在自适应时间窗口内查询,因此范围内出现较短或空白的页面属于正常情况:持续使用 starting_after 翻页,直到响应表明没有下一页。

WhatsApp 使用相同的 sms.* 事件族(共用一条流水线;channel 负载字段为 sms 或 whatsapp),因此某个电话号码的时间线可能混合两种传输方式。本指南介绍短信——添加 payload_filter[channel]=sms 将结果限定为短信,或查看 channel 字段来区分。

也可以直接调用 Backend API:

SINCE=$(( ($(date +%s) - 86400) * 1000 ))
curl -s "https://api.clerk.com/v1/logs?type=sms.failed&event_time_after=${SINCE}&limit=20" \
  -H "Authorization: Bearer $CLERK_SECRET_KEY" \
  | python3 -c "import sys,json; print(json.dumps(json.load(sys.stdin), indent=2))"

主要查询参数(除非某个操作步骤需要,否则均为可选):

参数用途
type事件类型:具体类型(sms.failed)或尾随通配符(sms.*)。使用 payload_filter 时必需。
payload_filter[<field>]精确匹配负载字段,例如 payload_filter[user_id]=user_123。对值进行 URL 编码(E.164 电话号码中的 + 应转换为 %2B)。
trace_id关联同一条短信的所有事件。
event_time_after / event_time_beforeUnix 毫秒时间范围。每次查询至少设置 event_time_after——见上文说明。
limit、starting_after、ending_before游标分页。

默认情况下,列表响应不包含解码后的负载;添加 payload_fields (以逗号分隔的叶路径)可包含指定字段,也可以通过 GET /v1/logs/{event_time_ms}:{event_id} 完整获取某一行。

reason、raw_error 和 rejected_before_send 仅存在于失败事件中,因此只有在 type 为具体类型 sms.failed 或 sms.undeliverable 时,才能选择或筛选这些字段。在 sms.* 通配符下,payload_fields 和 payload_filter 会根据五种架构的交集(公共字段:phone_number、user_id、phone_number_id、slug、verification_id、source_type、purpose、channel)进行验证,因此在此处请求 reason 会返回 422。使用 sms.* 查看某个号码的完整时间线;切换到 type=sms.failed 以查看失败原因。使用 GET /v1/logs/schemas?type=sms.failed(或使用 ?type=sms.* 查看交集)可发现任意类型允许使用的确切字段。

这些 logs 端点较新,且需要在实例上启用相应功能;如果调用返回 404 或没有结果,请先确认该实例已启用短信日志,再将空结果视为“没有这条短信”。

五种生命周期事件

一条短信会经历这一系列事件。每个事件只会在映射状态首次变化时发出一次,因此,送达迅速的短信可能会直接进入 sms.delivered,而不会出现可见的中间状态。

事件含义是否为终态?
sms.accepted送达服务提供商已接受交接。不能证明短信已送达手机。否
sms.delivered运营商已确认短信送达手机。是(成功)
sms.failed短信在交接前停止发送,或被服务提供商直接拒绝。包含 reason。是(失败)
sms.undeliverable运营商报告短信无法送达,此时服务提供商已接受交接。包含 reason。是(失败)
sms.unconfirmed服务提供商明确报告结果未知或未确认。没有任何一方的送达证据。否——后续的 delivered / undeliverable 仍可取代此状态

理解方式:accepted → delivered 是理想路径。failed 表示短信未能发出(或在入口处被拒);undeliverable 表示短信已发出,但被运营商退回;unconfirmed 表示结果无人知晓。

调试指南

“用户从未收到验证码。” 找到这条短信——查询 sms.*,按电话号码或用户 ID 过滤(payload_filter[phone_number] / payload_filter[user_id],并按上文所述添加 payload_filter[channel]=sms,以排除 WhatsApp)。然后查看该短信的最新事件:

  1. 完全没有 sms.* 事件 → 日志无法证明曾尝试发送短信。先排查查询是否有遗漏:确认已为此实例启用短信日志、时间范围涵盖发送时间,并且已翻页至游标末尾(这些日志尽力提供,详见“范围”)。如果日志可信且仍为空,原因通常出在上游:验证码未创建,或该号码在进入短信管线前已被屏蔽(检查 Protect / 机器人检测事件)。这不是短信送达问题。
  2. sms.failed → 查看 reason。如果存在 rejected_before_send,说明 Clerk 已阻止发送(国家/地区屏蔽、月度限额、速率限制)——应调整配置,而不是联系运营商。否则是服务提供商拒绝了发送;请参阅原因表。
  3. 有 sms.accepted,但没有 sms.delivered → 短信已离开 Clerk,但运营商始终未确认。稍等片刻(送达回执可能延迟),如果长时间仍无回执,则将其视为该号码的运营商或手机问题。
  4. sms.undeliverable → 运营商在接受短信后将其退回。查看 reason;destination_unreachable / invalid_phone_number 表明问题出在号码本身。
  5. sms.unconfirmed → 目前没有送达信号。它不是终态:后续的 sms.delivered 或 sms.undeliverable 仍可取代此状态,因此在得出结论前,请重新查询追踪记录。不要据此推断成功或失败;如果尚未收到确定事件,且用户也没有收到短信,请让他们重试。

“发往 的短信无法发送。” 查找 reason = country_not_supported 的 sms.failed(Clerk 屏蔽了该国家/地区),或 reason = restricted_destination(服务提供商不会向该地区发送短信)。如果 reason = rate_limited 且设置了 rejected_before_send 的事件激增,说明这是 Clerk 自身针对号码或号段的限流——在短信轰炸攻击期间属于预期行为,并非服务中断。

解读失败原因

sms.failed 和 sms.undeliverable 都会携带固定集合中的标准化 reason(可过滤且稳定的契约),以及可选的 raw_error(服务提供商报告的原始错误信息;有报告时会包含,可用于排查特定情况,但因其因服务提供商而异且不稳定,切勿据此过滤)。

完整的原因列表及各值的含义——包括哪些原因只会出现在 Clerk 侧发送前拒绝的情况中——请参阅 references/events.md。

关联一条消息的生命周期

同一条 SMS 的所有事件共享同一个追踪记录。一条消息有多个事件时(例如,先是 accepted,然后是 undeliverable),即使它们的主题可能不同,也会通过同一个追踪 ID 关联起来。使用追踪记录来整理一条消息的时间线,而不是孤立地查看各个事件。

范围与保证

  • 仅限由 Clerk 投递的 SMS。 如果你使用自己的 SMS 服务提供商(delivered_by_clerk = false),这些事件并不能描述你发送的消息。
  • 负载中绝不会包含消息正文、验证码或服务提供商的身份信息。 请根据 reason、时间戳以及电话号码 / 用户 ID 进行排查,而不是依赖其中不存在的消息内容。
  • 尽力而为的遥测。 与所有应用程序日志一样,这些事件不保证一定送达;缺少事件只能作为较弱的证据。不要构建依赖每个 SMS 事件都能送达的应用逻辑;对于应用必须据此采取行动的投递状态,请使用验证本身的状态。
  • protect.sms.*(Clerk Protect 的反滥用拦截)属于单独的事件系列;Clerk 端的拦截会显示在该系列中,而不是作为 sms.failed 出现。

完整的事件 / 原因 / 负载参考:references/events.md。

评论

支持 Markdown