Twilio SDKでSMS送信機能を実装する入門
概要
アプリケーションから SMS を送りたい場面は意外と多いものです。予約リマインド、二要素認証(2FA)のコード通知、配送ステータスの連絡など、メールよりも到達率と即時性が求められるシーンで SMS は強みを発揮します。
本記事では、クラウド通信プラットフォームである Twilio の公式 SDK を使い、プログラムから SMS を送信する最小構成を入門者向けにまとめます。Node.js と Python の両方のサンプルを示し、送達状況の確認方法や実運用での注意点まで扱います。
Twilio とは
Twilio は SMS・音声通話・ビデオ・メールなどの通信機能を API で提供する CPaaS(Communications Platform as a Service)です。自前でキャリアと契約したり通信ゲートウェイを構築したりせず、HTTP API 一本で各国のキャリア網にメッセージを流せる点が最大の利点です。
SMS 送信に関わる主要な概念は次の 3 つです。
| 概念 | 役割 |
|---|---|
| Account SID | アカウントを識別する ID(AC 始まり) |
| Auth Token | API 認証用のシークレット(パスワード相当) |
| From(送信元番号) | Twilio で取得した送信元の電話番号 |
Account SID と Auth Token はコンソールのダッシュボードで確認できます。Auth Token は実質的なパスワードなので、ソースコードやリポジトリに直書きしないことが鉄則です。
環境
本記事のサンプルは以下の前提で記述しています。
- Node.js 20.x /
twilioパッケージ v6 系(v6 は最低要件が Node.js 20 以上に引き上げられています。本記事の 20.x はこの要件を満たします) - Python 3.11 /
twilioパッケージ 9.x 系 - Twilio アカウント(無料トライアルで可)
- 送信元となる Twilio 電話番号
トライアルにはクレジットが付与され動作検証が可能ですが、後述の制約があります(仕様は変わり得るため公式を確認してください)。
事前準備
1. アカウントと電話番号の取得
- Twilio のコンソールでアカウントを作成する
- ダッシュボードで Account SID と Auth Token を控える
- 「Phone Numbers」から SMS 対応の番号を 1 つ取得する
2. 認証情報を環境変数に置く
認証情報は環境変数で渡すのが基本です。.env に置き、リポジトリにはコミットしません。
# .env (リポジトリにコミットしないこと) TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your_auth_token_here TWILIO_FROM_NUMBER=+1xxxxxxxxxx
.gitignore に .env を必ず追加します。
実装例
Node.js での送信
npm install twilio dotenv
// send-sms.js
import 'dotenv/config';
import twilio from 'twilio';
const client = twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
async function sendSms(to, body) {
const message = await client.messages.create({
from: process.env.TWILIO_FROM_NUMBER,
to, // E.164 形式: +819012345678
body,
});
return message.sid;
}
sendSms('+819012345678', 'テスト送信です。')
.then((sid) => console.log('送信成功 SID:', sid))
.catch((err) => console.error('送信失敗:', err.code, err.message));
宛先 to は E.164 形式(国番号付きの +819012345678 のような形式)で渡すのが必須です。国内形式の 090-1234-5678 のままでは弾かれます。
Python での送信
pip install twilio python-dotenv
# send_sms.py
import os
from dotenv import load_dotenv
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
load_dotenv()
client = Client(
os.environ["TWILIO_ACCOUNT_SID"],
os.environ["TWILIO_AUTH_TOKEN"],
)
def send_sms(to: str, body: str) -> str:
message = client.messages.create(
from_=os.environ["TWILIO_FROM_NUMBER"],
to=to, # E.164 形式
body=body,
)
return message.sid
if __name__ == "__main__":
try:
sid = send_sms("+819012345678", "テスト送信です。")
print("送信成功 SID:", sid)
except TwilioRestException as e:
print("送信失敗:", e.code, e.msg)
Python では予約語との衝突を避けるため、送信元パラメータが from_(末尾アンダースコア)になっている点に注意してください。
確認方法
送信結果の即時確認
messages.create が返す sid(SM で始まる)がメッセージの識別子です。成功時はこれが取得でき、以降の状態照会に使えます。
送達ステータスの照会
SMS は「API が受理した(queued / accepted)」ことと「端末に届いた(delivered)」ことが別物です。受理後の状態は後から照会できます。
const msg = await client.messages('SMxxxxxxxx').fetch();
console.log(msg.status); // queued / sent / delivered / failed / undelivered
console.log(msg.errorCode);
| status | 意味 |
|---|---|
queued |
受理し送信待ち |
sent |
キャリアへ送出済み |
delivered |
端末への到達を確認 |
undelivered |
送出したが届かなかった |
failed |
送信自体が失敗 |
Webhook で非同期に受け取る
本番では statusCallback にエンドポイント URL を指定し、ステータス変化を Webhook で受け取るのが定石です。
await client.messages.create({
from: process.env.TWILIO_FROM_NUMBER,
to,
body,
statusCallback: 'https://example.com/webhooks/twilio/status',
});
受け取り側では X-Twilio-Signature ヘッダで署名検証を行い、なりすましを防ぎます。
注意点
- トライアル制約: 送信先は検証済み番号のみ、本文に試用文言が自動付与されます。本番前にアカウントのアップグレードが必要です。
- E.164 形式の徹底: 宛先は
+国番号から。保存・送信前にlibphonenumber等で正規化すると堅牢です。 - エラーを握りつぶさない: エラーコード(例:
21211は宛先不正、21608は未検証番号宛て)をコードとメッセージまでログに残します。 - レート制限と冪等性: 大量送信はキュー経由にし、再送時は冪等キーで二重送信を防ぎます。
- コストと法令: 1 通ごとに課金され、国・キャリアで単価が異なります。広告 SMS はオプトインや送信時間帯の規制に注意します。
- シークレット管理: Auth Token はパスワード相当。環境変数やシークレットマネージャで管理し、漏洩時はコンソールから速やかにローテーションします。
まとめ
- Twilio SDK なら Account SID / Auth Token / 送信元番号の 3 点で SMS 送信を始められる
- 宛先は E.164 形式に正規化する
- 「受理」と「送達」は別物。本番では
statusCallback(Webhook)で送達状況を非同期に追う - トライアルには検証済み番号のみ・文言付与の制約がある
- Auth Token は環境変数/シークレットマネージャで管理し、直書きしない
まずは 1 通送るところから始め、ステータス照会と Webhook 受信、番号正規化、エラーハンドリングの順に肉付けしていくと、実運用に耐える SMS 機能へ無理なく育てられます。
