電話でつなぐ
Twilio の
電話番号と<Connect><Stream> のaction の
着信 → サービスの TwiML(<Connect><Stream>)→ voicast の AI が応対
→ AI が会話を終える → Twilio が action を呼ぶ → 結果を見て <Dial> か <Hangup/>TwiML#
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Connect action="https://saas.example.com/twilio/after">
<Stream url="wss://voice.voicast.jp/ws">
<Parameter name="tenant" value="ten_…" />
<Parameter name="secret" value="vts_…" />
<Parameter name="from" value="+819012345678" />
</Stream>
</Connect>
</Response>| Parameter | 必須 | 内容 |
|---|---|---|
tenant |
○ | テナントのten_…) |
secret |
○ | テナントのvts_…。 |
agent |
会話のagt_…) |
|
from |
かけてきたfrom に |
Twilio の<Parameter> はfrom などは
Node.js の例#
Twilio のhttps://saas.example.com/twilio/voice を
import express from 'express';
const app = express();
app.use(express.urlencoded({ extended: false }));
const xml = (s) => String(s).replace(/[<>&"']/g, (c) => `&#${c.charCodeAt(0)};`);
// 着信: かかってきた番号(To)から、自社の顧客と voicast のテナントを引く
app.post('/twilio/voice', async (req, res) => {
const t = await findTenantByNumber(req.body.To); // { voicastTenantId, voicastSecret }
res.type('text/xml').send(`<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Connect action="https://saas.example.com/twilio/after">
<Stream url="wss://voice.voicast.jp/ws">
<Parameter name="tenant" value="${xml(t.voicastTenantId)}" />
<Parameter name="secret" value="${xml(t.voicastSecret)}" />
<Parameter name="from" value="${xml(req.body.From)}" />
</Stream>
</Connect>
</Response>`);
});引き継ぎ#
AI がoutcome)<Connect> のaction をCallSid 付きでGET /v1/calls/by-provider/{CallSid} で
// AI の応対のあと: 結果を見て、人に転送するか切る
app.post('/twilio/after', async (req, res) => {
const r = await fetch(`https://api.voicast.jp/v1/calls/by-provider/${req.body.CallSid}`, {
headers: { Authorization: `Bearer ${process.env.VOICAST_API_KEY}` },
});
const call = r.ok ? await r.json() : null;
// 記録が無い(つながらなかった)・引き継ぎ・途中の障害は、人につなぐ
const toHuman = !call || call.outcome === 'handoff' || call.outcome === 'error';
res.type('text/xml').send(
toHuman
? '<Response><Say language="ja-JP">担当者におつなぎします。</Say><Dial>+81312345678</Dial></Response>'
: '<Response><Hangup/></Response>',
);
});outcome |
次の |
|---|---|
handoff |
担当者・<Dial> |
completed・answered |
<Hangup/>fields と |
error |
人へ |
hangup |
何もしない |
| 記録が |
人へ |
引き継ぐreason に、fields にcall.handoff_requested でもaction で
断られたとき#
次のaction に
tenantかsecretが違う、 テナントが 止まっている ( status: disabled)- 残高が
0 以下・ 支払いの 遅れ・ 使いすぎの 上限で、 新しい 通話を 断っている とき (料金と 請求 )
始まった
鍵の作り直し#
POST /v1/tenants/{id}/secret で
音声と動き#
- 音声は
Twilio の Media Streams (8kHz の μ-law) です。 voicast が Twilio の REST で 通話を 切る ことは ありません (通話の 制御は サービスの 側の Twilio が 持ちます) 。 - 相手が
話し始めると、 AI は 話している 途中でも 止めて 聞きます。 - 相手が
しばらく 黙っていると 聞き直し、 それでも 返事が 無ければ hangupで終えます。 - 同じ
CallSidで音声の 接続が つなぎ直された とき (音声サーバーの 再起動など) は、 同じ 通話の 記録を 使い続けます。
クラウドの電話・CTI から#
Twilio の<Connect><Stream> をaction で
- 番号ごと
・時間帯ごとに テナントや 会話の 設定 ( agent)を 替えられます。 夜間だけ AI に 出て もらう、 と いった 使い方が できます。 actionを置けない 構成では、 </Connect>のあとに 続けたい 動詞 ( <Dial>など)を 書きます。 音声の 接続が 閉じると Twilio は 次の 動詞に 進みます。 - Twilio の
Media Streams 以外の 方式 (SIP など) で つなぎたい ときは、 お問い合わせ ください。
試すとき#
テストの