clerk-sms-logs について
clerk-sms-logsは、Clerkが送信したSMSの配信状況を調べるスキルです。開発者はApplication Logsを使い、Clerk側のブロック、プロバイダーの拒否、キャリアの配信問題、未確定の結果を区別できます。
主な機能
- 015種類のSMSライフサイクルイベントを解説
- 02DashboardまたはBackend APIでログを照会
- 03時刻、電話番号、ユーザー、traceで絞り込み
- 04失敗の診断手順を提示
- 05イベントの対象範囲とテレメトリーの制約を説明
活用シーン
- 01届かなかった認証コードの調査
- 02送信前の拒否とキャリア障害の切り分け
- 03電話番号ごとのSMS履歴の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-sms-logsインストール後、Claude Code で/clerk-sms-logsを入力して読み込まれたか確認します。
同梱ファイルreferences/events.md
詳細
SMS ログ
質問に答えます。この Clerk 配信の SMS に何が起きたのでしょうか? sms.*
Application Logs には、Clerk がインスタンスに代わって送信するすべての SMS の配信ライフサイクルが記録されます。電話番号の確認コード、OTP、パスワードリセットコードなどを確認できるため、サインアップの停滞、キャリアによる拒否、Clerk 側でのブロックを推測せずに見分けられます。
ログは 2 通りの方法で確認できます。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(SMS の送信時刻がおおよそ分かる場合は event_time_before も)を指定し、見つからない場合にのみ範囲を広げます。1 件のメッセージを探すために保持期間全体をスキャンしないでください。limit は控えめな値(10~50)にし、値を大きくするのではなく、返されたカーソルを使ってページを進めてください。エンドポイントは適応的な時間ウィンドウでクエリを実行するため、指定範囲内でページの内容が少なかったり、空だったりするのは正常です。レスポンスに次のページがないことが示されるまで starting_after を指定して取得してください。
WhatsApp も同じ sms.* ファミリーを使います(パイプラインは共通で、ペイロードの channel フィールドは sms または whatsapp です)。そのため、電話番号のタイムラインには両方の通信手段が混在する場合があります。このスキルでは SMS を扱います。対象を絞るには 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 | 1 件のメッセージに関するすべてのイベントを関連付けます。 |
event_time_after / event_time_before | Unix ミリ秒単位の範囲指定です。上記の注記のとおり、すべてのクエリで少なくとも event_time_after を指定してください。 |
limit、starting_after、ending_before | カーソルによるページネーションです。 |
一覧レスポンスでは、デコード済みのペイロードはデフォルトで省略されます。特定のフィールドを含めるには payload_fields(カンマ区切りのリーフパス)を追加してください。または GET /v1/logs/{event_time_ms}:{event_id} で 1 行の全データを取得できます。
reason、raw_error、rejected_before_send は失敗イベントにのみ存在するため、選択やフィルターに使用できるのは type が具体的な sms.failed または sms.undeliverable の場合のみです。sms.* ワイルドカードでは、payload_fields と payload_filter は 5 つすべてのスキーマの 共通部分(共通フィールドの 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 は存在しない」と判断する前に、インスタンスで SMS ログが有効になっていることを確認してください。
5 つのライフサイクルイベント
1 件の SMS は、この一連のイベントをたどります。各イベントは、対応する状態が初めて変化したときに 1 回だけ発行されます。そのため、配信が速い場合は、中間のイベントが表示されずに sms.delivered へ直接進むことがあります。
| イベント | 意味 | 終端状態? |
|---|---|---|
sms.accepted | 配信プロバイダーが引き渡しを受け付けました。電話に届いたことを証明するものではありません。 | いいえ |
sms.delivered | 携帯電話会社が端末への配信を確認しました。 | はい(成功) |
sms.failed | 引き渡しの前に送信が停止したか、プロバイダーが送信を拒否しました。reason を含みます。 | はい(失敗) |
sms.undeliverable | プロバイダーが受け付けた後、携帯電話会社が配信できなかったことを報告しました。reason を含みます。 | はい(失敗) |
sms.unconfirmed | プロバイダーが結果を明示的に不明または未確認と報告しました。配信を示す証拠は、どちらの側にもありません。 | いいえ — 後続の delivered/undeliverable がこの状態を置き換えることがあります |
基本的な流れは、accepted → delivered が正常な経路です。failed は送信が外部に出なかった(または入口で拒否された)ことを意味し、undeliverable は送信されたものの携帯電話会社に拒否されたことを意味します。unconfirmed は結果が誰にもわからないことを意味します。
デバッグ手順
「ユーザーにコードが届きませんでした。」 メッセージを見つけるには、電話番号またはユーザー ID で絞り込んで sms.* を検索します(payload_filter[phone_number]/payload_filter[user_id] に加えて、上記のセクションに従い payload_filter[channel]=sms を指定して WhatsApp を除外します)。そのメッセージの最新イベントを確認します。
sms.*イベントがまったくない → ログからは、送信が試みられたことを確認できません。まず、クエリに漏れがないか確認してください。インスタンスで SMS ログが有効になっていること、時間範囲に送信時刻が含まれていること、カーソルを最後までページングしたことを確認します(これらのログはベストエフォートです。「対象範囲」を参照してください)。ログが信頼できる状態で、それでも空の場合、通常は上流に原因があります。認証が作成されていなかったか、SMS パイプラインに入る前に電話番号がブロックされていた可能性があります(Protect/ボット検出イベントを確認してください)。これは SMS の配信問題ではありません。sms.failed→reasonを確認します。rejected_before_sendが含まれている場合は Clerk が停止しています(国によるブロック、月間上限、レート制限)。対処すべきなのは設定であり、携帯電話会社ではありません。それ以外の場合はプロバイダーが拒否しています。理由の表を参照してください。sms.acceptedはあるがsms.deliveredがない → Clerk から送信されましたが、携帯電話会社から確認が届いていません。少し待ってください(配信確認は遅れることがあります)。それでも状況が変わらない場合は、その電話番号に関する携帯電話会社または端末の問題として扱ってください。sms.undeliverable→ 受け付けられた後、携帯電話会社が配信を拒否しました。reasonを確認します。destination_unreachable/invalid_phone_numberは、電話番号自体に問題があることを示します。sms.unconfirmed→ まだ配信シグナルがありません。これは終端状態ではありません。後続のsms.deliveredまたはsms.undeliverableがこの状態を置き換えることがあるため、結論を出す前にトレースを再検索してください。成功または失敗を推測しないでください。確定的なイベントが届かず、ユーザーにも届いていない場合は、再試行してもらってください。
「 への SMS 送信が機能しません。」 reason = country_not_supported(Clerk がその国をブロック)または restricted_destination(プロバイダーがその宛先への送信を拒否)を伴う sms.failed を探します。rejected_before_send が設定された reason = rate_limited が急増している場合は、Clerk 独自の電話番号/プレフィックス単位のスロットリングです。SMS ポンピング攻撃時には想定される挙動であり、障害ではありません。
失敗の読み取り方
sms.failed と sms.undeliverable には、固定セットから正規化された reason(フィルター可能で安定した契約)と、任意の raw_error(プロバイダーが報告した場合の独自の文言)が含まれます。raw_error は個別のケースでは役立ちますが、プロバイダー固有で不安定なため、フィルターには使用しないでください。
各値の意味を含む reason の全一覧と、Clerk 側で送信前に拒否された場合にのみ現れる理由については、references/events.md を参照してください。
1 件のメッセージのライフサイクルを関連付ける
1 件の SMS に関するすべてのイベントは、同じ トレースを共有します。1 件のメッセージに複数のイベント(例:accepted の後に undeliverable)がある場合、サブジェクトが異なっていても、同じトレース ID の下で関連付けられます。イベントを個別に読み取るのではなく、トレースを使って 1 件のメッセージのタイムラインを組み立ててください。
対象範囲と保証
- Clerk が配信する SMS のみです。 独自の SMS プロバイダーを使用している場合(
delivered_by_clerk = false)、これらのイベントは送信内容を表しません。 - ペイロードにメッセージ本文、確認コード、プロバイダーの識別情報が含まれることはありません。 存在しないメッセージ内容ではなく、
reason、タイムスタンプ、電話番号 ID、ユーザー ID を使ってデバッグしてください。 - ベストエフォートのテレメトリーです。 すべての Application Logs と同様に、これらのイベントの配信は保証されません。イベントがないことは弱い証拠にすぎません。すべての SMS イベントが届くことに依存するアプリケーションロジックを構築しないでください。アプリが対応する必要のある配信状態については、確認のステータスを使用してください。
protect.sms.*(Clerk Protect の不正利用防止ブロック)は別のイベント群です。Clerk 側のブロックはsms.failedではなく、こちらに表示されます。
イベント、理由、ペイロードの詳細なリファレンス:references/events.md。