OpenAI との接続ガイド
OpenAI 側(外部サービス)の設定を含む、クラコールSIP trunkとの接続手順です。
公開日 更新日
- このガイドでは、クラコールSIP trunkとの接続に必要なOpenAI 側(お客様が契約された外部サービス)の設定手順もご案内します。
- 各社の画面や仕様は変わることがあります。最新の内容は、各社の公式ドキュメントをご確認ください。
お客様がご自身で契約された OpenAI の Realtime API(SIP 接続)に、クラコールSIP trunkをつなぐ手順です。OpenAI の Realtime API(SIP)は、着信を受ける仕組みです。対応しているのは OpenAI の Realtime API(SIP による着信)です。Live API(GPT-Live)には対応していません。
OpenAI との接続には TLS・SRTP が必要なため、暗号化の有料オプションのお申し込みが必要です。通信の暗号化(TLS・SRTP)は有料オプション(料金はお問い合わせください)です。標準ではご利用いただけません。
通話の流れ
通話は必ずクラコールの SBC(東京・大阪)を通ります。OpenAI の Realtime API の SIP は着信を受ける仕組みのため、通話の向きは着信の1つです。
着信日本の電話から、OpenAI へ
- 電話回線日本
- クラコールの SBC東京・大阪(TLS・SRTP)
- sip.api.openai.comOpenAI の SIP の宛先(手順4)
- Project の Webhookrealtime.call.incoming(手順2)
- お客様のサーバーaccept で受け入れる(手順3)
クラコールの SBC は、着信を OpenAI の SIP の宛先(sip:[email protected];transport=tls)へ送ります。OpenAI は Webhook でお客様のサーバーに知らせ、お客様のサーバーが accept を送ると、着信がつながります。OpenAI の Realtime API の SIP は着信のみです(参考:Live API の発信は、Opus の音声と SIP のダイジェスト認証を使います。Live API(GPT-Live)には、クラコールは対応していません)。
どの接続先にも共通のルール(認証・電話番号の形式など)は、接続の基本ルールをご覧ください。
つなぐ手順
「0 はじめに用意するもの」をそろえてから、次の5つの手順(1〜5)で進めます。各手順には、作業者(クラコール/お客様)を記載しています。
-
0お客様
はじめに用意するもの
- クラコールSIP trunk(BYOC 接続)のご契約
- 暗号化の有料オプションのお申し込み
- OpenAI のアカウントとプロジェクト(お客様がご自身で契約されたもの)と、API キー
- インターネットから OpenAI の Webhook を受けられるお客様のサーバーと、Python 3.9 以上
- ご利用になる電話番号(+81 から始まる E.164 形式)
-
1
クラコール ⇄ お客様お申し込みと、接続に使う情報のやりとり
お申し込み後に、クラコールから接続に使う情報をお渡しします。お客様からは、OpenAI 側の情報をお知らせください。
クラコールからお渡しする情報
- お客様専用のドメイン(例:
xyz.customer.clocall.jp。実際のドメイン名はお申し込み後にお知らせします) - ご利用いただく電話番号(+81 から始まる E.164 形式)
- 使える方式:UDP・TCP(暗号化の有料オプションをお申し込みの場合は TLS・SRTP)
お客様からお知らせいただく情報
- OpenAI の SIP の宛先(SIP URI。
sip:[email protected];transport=tls。手順4) - 使う方式とポート番号(OpenAI では TLS・
5061) - 暗号化(TLS・SRTP)の有料オプション(OpenAI では必須)
- ご利用になる電話番号(+81 から始まる E.164 形式)
お問い合わせの本文に「BYOC 接続」とお書きのうえ、お送りください。BYOC では、着信・発信とも、INVITE の宛先(Request-URI)・To・From の電話番号を +81 から始まる E.164 形式にしてください(例:03-1234-5678 →
+81312345678)。- お客様専用のドメインには複数の IP アドレス(東京・大阪)が登録されています。返ってくるアドレスはすべて使えるようにしてください。
- SIP の REFER(通話の転送)には、クラコールは対応していません(接続の基本ルール)。OpenAI の
refer(SIP の REFER による転送)は、クラコール経由では使えません。
- お客様専用のドメイン(例:
-
2
お客様(OpenAI)Webhook を登録する
OpenAI は、着信があると
realtime.call.incomingというイベントを Webhook で知らせます。OpenAI のsettings > Project > Webhooks(platform.openai.com)を開き、Create(作成)のボタンで Webhook を登録します。入れるのは、次の3つです。- 名前(ご自身が見分けるためのものです)
- お客様のサーバーの公開 URL(手順3で動かすサーバーのアドレスです)
- 受け取るイベントの種類:
realtime.call.incoming
Webhook を作ると、
signing secret(署名用のシークレット)が表示されます。この値はあとからは見られないので、必ず保存してください。手順3で、この値を環境変数OPENAI_WEBHOOK_SECRETに入れます。なくしたとき、うっかり公開してしまったときは、rotating the signing secret(シークレットの再発行)で新しいものを作れます。- OpenAI から届く知らせは、本文の
typeがrealtime.call.incomingのイベントで、通話の ID はdataの中のcall_idです。ヘッダーにはwebhook-id・webhook-timestamp・webhook-signatureが付きます。 - 知らせを受けたお客様のサーバーは、すぐに 2xx(成功)の応答を返します。2xx でないときや応答が遅いときは、OpenAI は同じ知らせを再送します(最長 72 時間)。
- 同じ知らせが重複して届くことがあります。
webhook-idを重複を見分ける印(idempotency key)に使えます。
参考(公式):OpenAI 公式ドキュメント:Telephony and SIP(英語)
このページのOverviewの項に、realtime.call.incomingの本文の例と、settings > Project > Webhooksの説明があります。
OpenAI 公式ドキュメント:Webhooks(英語)
このページのCreating webhook endpointsの項に、Create・名前・公開 URL・イベントの種類・signing secretの説明があります。同じページのHandling webhook requests on a serverの項に、2xx の応答と再送・webhook-idの説明があります。 -
3
お客様(サーバー)着信を受け入れるサーバーを動かす(webhook.py)
次の
webhook.pyは、Webhook を受けて署名を確かめ、realtime.call.incomingならacceptで着信を受け入れます。Python の標準ライブラリだけで動くので、追加のインストールは要りません(Python 3.9 以上)。webhook.py
import base64 import hashlib import hmac import json import os import time import urllib.error import urllib.request from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer OPENAI_BASE = os.environ.get("OPENAI_BASE", "https://api.openai.com") OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] WEBHOOK_SECRET = os.environ["OPENAI_WEBHOOK_SECRET"] PORT = int(os.environ.get("PORT", "8000")) TOLERANCE_SECONDS = 300 # シークレットが whsec_ で始まる場合は、その後ろを base64 として復号します(起動時に1回だけ) if WEBHOOK_SECRET.startswith("whsec_"): try: KEY = base64.b64decode(WEBHOOK_SECRET[6:], validate=True) except ValueError: raise SystemExit("OPENAI_WEBHOOK_SECRET が正しくありません。OpenAI の画面に表示された signing secret をそのまま設定してください。") else: KEY = WEBHOOK_SECRET.encode() # 着信を受け入れるときに送る内容(値は OpenAI 公式の例です。お客様の用途に合わせて変えてください) ACCEPT_BODY = { "type": "realtime", "model": "gpt-realtime-2.1", "instructions": "You are Alex, a friendly concierge for Example Corp.", } def signature_is_valid(headers, body): # Standard Webhooks の署名検証:msg_id.timestamp.payload を HMAC-SHA256 で署名し、base64 にしたものを比べます msg_id = headers.get("webhook-id", "") timestamp = headers.get("webhook-timestamp", "") signatures = headers.get("webhook-signature", "") try: if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS: return False except ValueError: return False signed = msg_id.encode() + b"." + timestamp.encode() + b"." + body expected = base64.b64encode(hmac.new(KEY, signed, hashlib.sha256).digest()).decode() for item in signatures.split(): version, _, value = item.partition(",") if version == "v1" and hmac.compare_digest(value.encode(), expected.encode()): return True return False def accept_call(call_id): accept_endpoint = f"{OPENAI_BASE}/v1/realtime/calls/{call_id}/accept" request = urllib.request.Request( accept_endpoint, data=json.dumps(ACCEPT_BODY).encode(), headers={ "Authorization": f"Bearer {OPENAI_API_KEY}", "Content-Type": "application/json", }, method="POST", ) with urllib.request.urlopen(request, timeout=10) as response: return response.status class Handler(BaseHTTPRequestHandler): def do_POST(self): body = self.rfile.read(int(self.headers.get("Content-Length", "0"))) if not signature_is_valid(self.headers, body): self.send_response(400) self.end_headers() return event = json.loads(body) if event.get("type") == "realtime.call.incoming": call_id = event["data"]["call_id"] try: accept_call(call_id) print("accepted", call_id) except urllib.error.HTTPError as error: if error.code == 429 or error.code >= 500: # 一時的なエラー:502 を返し、OpenAI に再送してもらいます print("accept failed", call_id, error.code) self.send_response(502) self.end_headers() return # それ以外で断られた場合(恒久的):ログに残し、Webhook には 200 を返します(再送を避けるため) print("accept refused", call_id, error.code) except OSError as error: # ネットワークの失敗・タイムアウト:502 を返し、OpenAI に再送してもらいます print("accept failed", call_id, error) self.send_response(502) self.end_headers() return self.send_response(200) self.end_headers() ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()ターミナル
export OPENAI_API_KEY="(OpenAI の API キー)" export OPENAI_WEBHOOK_SECRET="(Webhook の署名用のシークレット)" python3 webhook.pyOPENAI_API_KEY:OpenAI の API キーです。acceptの呼び出しのAuthorization: Bearerに使います。コードには書かず、環境変数で渡します。OPENAI_WEBHOOK_SECRET:手順2で保存したsigning secretです。コードは、値がwhsec_で始まる場合は、その後ろを base64 として復号して鍵にし、そうでない場合は、値をそのまま鍵にします。- 署名の検証は、Standard Webhooks の仕様のとおり、
webhook-id・webhook-timestamp・本文を.でつないだものを HMAC-SHA256 で署名し、webhook-signatureのv1,のあとの値と比べます。時刻が許容差(例では5分)を超えるものと、署名が合わないものは、400で断ります。 acceptの本文のtypeはrealtimeです。modelとinstructionsの値は、OpenAI の公式の例のものです。お客様の用途に合わせて変えてください。acceptが成功すると、OpenAI は200 OKを返し、通話の準備が始まります。お客様のサーバーも200を返します。acceptが一時的なエラー(429・5xx)や通信エラーの場合は502を返して再送してもらい、それ以外で断られた場合は200を返します。- 例の
OPENAI_BASEは、試験用に宛先を切り替えるためのもので、通常は設定しません(標準はhttps://api.openai.comです)。
参考(公式):OpenAI 公式ドキュメント:Telephony and SIP(英語)
このページのAccept the callの項に、acceptの呼び出し(Authorization: Bearer・本文のtype・model・instructions)の例があります。
OpenAI 公式ドキュメント:Webhooks(英語)
このページのVerifying webhook signaturesの項に、OPENAI_WEBHOOK_SECRETと、署名を自分で検証する場合は Standard Webhooks の仕様に従う、という説明があります。
Standard Webhooks の仕様(英語)
このページのVerifying webhook authenticityの項(見出しは英語の仕様のもの)に、署名の対象・HMAC-SHA256・whsec_の説明があります。 -
4
お客様 → クラコールSIP の宛先をクラコールにお知らせする
クラコールの SBC が着信を送る先は、OpenAI の SIP の宛先(SIP URI)です。次の形で、
$PROJECT_IDをお客様のプロジェクト ID に置き換えたものをクラコールへお知らせください。OpenAI の SIP の宛先(クラコールの SBC が着信を送る先)
sip:[email protected];transport=tls$PROJECT_ID:OpenAI のプロジェクト ID(proj_の接頭辞が付いた文字列)に置き換えます。settings > Project > Generalで確認できます。- ポートは TCP/TLS の
5061です。TLS を使うため、暗号化の有料オプションが必要です。 - 着信の電話番号は、+81 から始まる E.164 形式で届きます。
クラコールとの接続では、Realtime API の SIP の音声の形式は G.711 μ-law
(PCMU)です。 参考(公式):OpenAI 公式ドキュメント:Telephony and SIP(英語)
このページのOverviewの項に、SIP の宛先(sip:[email protected];transport=tls)と$PROJECT_IDの説明があります。SIP signaling and media IP rangesの項に、ポート5061の説明があります。お知らせいただいた SIP の宛先を、クラコール側で着信の宛先に設定します。設定が終わったら、手順5で確認します。 -
5
クラコール ⇄ お客様つながることを確認する
- 着信:クラコールの電話番号へ、携帯電話などから電話をかけ、お客様のサーバー(
webhook.py)に、OpenAI からrealtime.call.incomingが届くことを確認します(例のコードは、受け入れるとacceptedと通話の ID を表示します)。 - 着信を受け入れたあと、相手の声が聞こえることを確認します。
うまくつながらないときは、うまくつながらないときをご覧ください。 - 着信:クラコールの電話番号へ、携帯電話などから電話をかけ、お客様のサーバー(
通信の暗号化(有料オプション)
通信の暗号化(TLS・SRTP)は有料オプション(料金はお問い合わせください)です。標準ではご利用いただけません。OpenAI との接続には TLS・SRTP が必要なため、お申し込みが必要です。お申し込みの場合の対応は次のとおりです。
- TLS:TLS 1.3・1.2 に対応しています。
- SRTP:RFC 3711 の RTP/SAVP です。RFC 5124 の RTP/SAVPF には対応していません。
- SRTP の暗号方式:AES_CM_128_HMAC_SHA1_80
- OpenAI の SIP の宛先は、末尾が
;transport=tlsの形で、ポートは TCP/TLS の5061です。
うまくつながらないとき
- 着信が届かない:SIP の宛先のプロジェクト ID が正しいか、暗号化の有料オプションをお申し込みか、確認してください。
- Webhook が届かない:
settings > Project > Webhooksに登録したか、realtime.call.incomingを受け取る設定か、送り先のサーバーがインターネットから届くか、確認してください。 - Webhook は届くが
400を返す:OPENAI_WEBHOOK_SECRETが正しいか、サーバーの時刻がずれていないか、確認してください。 - 着信は届くが、つながらない:
realtime.call.incomingを受け取ったあと、acceptで着信を受け入れているか、OPENAI_API_KEYが正しいか、確認してください。 - 上を確認してもつながらない場合は、お問い合わせの本文に「BYOC 接続」と書き、試した時刻をお知らせください。
OpenAI のマニュアル(英語)との用語対応
OpenAI のマニュアルで探すときの用語は、次のとおりです。
| このページ | OpenAI のマニュアル |
|---|---|
| 対応している API(着信) | Realtime API(対応していないのは Live API(GPT-Live)) |
| OpenAI の SIP の宛先(SIP URI) | sip:[email protected];transport=tls |
| プロジェクト ID | $PROJECT_ID(settings > Project > General で確認) |
| Webhook を登録する場所 | settings > Project > Webhooks(Create で登録) |
| 着信を知らせるイベント | realtime.call.incoming |
| 通話の ID | call_id(イベントの data の中) |
| 着信を受け入れる API | accept(POST /v1/realtime/calls/$CALL_ID/accept) |
| 署名用のシークレット | signing secret(OPENAI_WEBHOOK_SECRET) |
| Webhook の署名の検証 | webhook-signature(Standard Webhooks の方式) |
| 通話の転送 | refer |
- 記載の社名・サービス名は各社の商標または登録商標です。クラコールは各社と提携・協業しておらず、各社から推奨・承認・認定を受けているものではありません。
- クラコールはAI機能を提供するものではありません。OpenAI の利用条件・料金・動作は同社の定めによります。
- 最終確認日:2026.10.23(各社の公式ドキュメントを確認した日)
- 掲載内容は最終確認日時点の公開情報に基づくもので、各社の仕様・条件の変更により変わることがあります。動作・互換性・継続的な利用を保証するものではありません。


