> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tiro.ooo/llms.txt
> Use this file to discover all available pages before exploring further.

# クイックスタート

> リアルタイム文字起こしのセッションを作成し、WebSocketでPCM音声を送って文字起こしと翻訳の結果を受け取り、セッションを閉じるまでをPythonの例で試します。

エンタープライズ リアルタイム文字起こしAPIは、エンタープライズプランのお客様に個別のお問い合わせで提供する機能です。TiroがワークスペースでこのAPIを有効にすると、このガイドを試せます。

このガイドでは、音声ファイルを1つ実時間の速度で送り、文字起こしと翻訳の結果をターミナルに出力してから、セッションを閉じます。

## 始める前に

* リアルタイム文字起こしAPIが有効なワークスペース。条件は[概要](https://docs.tiro.ooo/ja/developers/realtime-transcription/overview.md#who-can-use)をご覧ください。
* そのワークスペースに紐づいたAPIキー。スコープを指定した場合は `realtime_transcription:write` が必要です。キーを環境変数 `TIRO_API_KEY` に設定してください。
* Python 3.9以上と `websocket-client` パッケージ。`pip install websocket-client` でインストールしてください。
* 音声ファイルをPCMに変換する `ffmpeg`。

**1. 音声を16 kHzモノラルのPCMに変換する**

リアルタイム文字起こしAPIは、16,000 Hz、モノラル、signed 16-bit little-endian PCMだけを受け付けます。ヘッダーのないrawデータである必要があります。`ffmpeg` で任意の音声ファイルをこの形式に変換してください。

```bash theme={"system"}
ffmpeg -i meeting.m4a -ac 1 -ar 16000 -f s16le -acodec pcm_s16le meeting.pcm
```

この形式は1秒あたり32,000バイトです。マイク入力をそのまま送る場合も同じ形式にしてください。

**2. セッションを作成する**

お客様のサーバーでセッションを作成してください。この例では韓国語を文字起こしし、英語に翻訳します。翻訳が不要なら `translationLocale` を外してください。

```bash cURL theme={"system"}
curl -X POST https://api.tiro.ooo/v1/external/realtime-transcriptions \
  -H "Authorization: Bearer $TIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transcriptionLocale": "ko_KR",
    "translationLocale": "en_US"
  }'
```

```python Python theme={"system"}
import json
import os
import urllib.request

request = urllib.request.Request(
    "https://api.tiro.ooo/v1/external/realtime-transcriptions",
    data=json.dumps({"transcriptionLocale": "ko_KR", "translationLocale": "en_US"}).encode(),
    headers={
        "Authorization": f"Bearer {os.environ['TIRO_API_KEY']}",
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request) as response:
    session = json.load(response)
print(session["id"], session["wsUrl"])
```

成功すると `201 Created` でセッションが返ります。

```json theme={"system"}
{
  "id": "Rt7Yq2PbX9cDk",
  "workspaceGuid": "Wk8sQ2mLx1aB7",
  "status": "OPEN",
  "endReason": null,
  "wsUrl": "wss://api.tiro.ooo/v1/external/realtime-transcriptions/Rt7Yq2PbX9cDk/stream",
  "streamToken": "rtt_5Hq3...",
  "maxDurationMs": 10800000,
  "audioDurationMs": 0,
  "transcriptionLocale": "ko_KR",
  "translationLocale": "en_US",
  "apiKeyId": "tk_8f2a91c0d3e4",
  "createdAt": "2026-11-05T09:00:00Z",
  "endedAt": null
}
```

`streamToken` はこのレスポンスでしか受け取れません。セッションが `OPEN` の間は再接続にも同じトークンを使うので、安全に保管してください。`wsUrl` は自分で組み立てず、受け取った値をそのまま使ってください。

**3. WebSocketで接続する**

クライアントから `wsUrl` に接続し、`Authorization` ヘッダーに `streamToken` を入れてください。クエリ文字列やCookieでトークンを送ることはできません。

```python theme={"system"}
import websocket

ws = websocket.create_connection(
    session["wsUrl"],
    header=[f"Authorization: Bearer {session['streamToken']}"],
    suppress_origin=True,
)
print(ws.recv())
```

接続すると、最初のメッセージとして `session.started` が届きます。通常は1秒前後で届き、まれに数十秒かかります。このメッセージを待たずに音声を送り始めてもかまいません。

```json theme={"system"}
{ "type": "session.started", "id": "Rt7Yq2PbX9cDk", "resumed": false, "audioOffsetMs": 0 }
```

**4. 音声を送って結果を受け取る**

音声を3,200バイト（100 ms）ずつに分け、バイナリフレームで送ってください。録音済みのファイルも実時間の速度で送る必要があります。実際の経過時間より60秒以上先に進むと、Tiroがclose `4029` で接続を閉じます。

結果は `transcript` と `translation` のテキストメッセージで届きます。同じ `segmentId` の `isFinal: false` メッセージは、画面の以前のテキストを丸ごと置き換えます。`isFinal: true` が届くとその文が確定します。

```json theme={"system"}
{ "type": "transcript", "segmentId": "Ab3kZ9qLm2XyW-1-17", "isFinal": true, "text": "오늘 회의는 일정 점검부터 하겠습니다.", "locale": "ko_KR" }
{ "type": "translation", "translationId": "t3-9", "isFinal": true, "locale": "en_US", "text": "Today's meeting will start with a schedule review.", "sourceSegmentId": null }
```

**5. セッションを閉じる**

最後の音声を送ったら `{"type": "session.end"}` を送ってください。Tiroが残りの結果をすべて `isFinal: true` で送り、`session.ended` を送ってからclose `1000` で接続を閉じます。`session.ended` を受け取るまで接続を維持してください。残りの結果の送信には最大30秒かかります。

```json theme={"system"}
{ "type": "session.ended", "id": "Rt7Yq2PbX9cDk", "endReason": "CLIENT_REQUESTED", "audioDurationMs": 3605120, "endedAt": "2026-11-05T10:01:30Z" }
```

WebSocketを使えない場合は、お客様のサーバーから `POST /v1/external/realtime-transcriptions/{id}/end` を呼び出してセッションを閉じてください。

> [!WARNING]
>
> セッションを終了せずにWebSocket接続だけを閉じると、セッションは `OPEN` のまま残ります。音声なしで15分経って自動で終了するまで、ワークスペースの同時セッション10件の枠を1つ占有します。

## 完全な例

次のスクリプトは上の手順を1つにまとめたものです。PCMファイルを実時間の速度で送り、結果を出力し、セッションを閉じます。接続が切れた場合は再接続せず、RESTでセッションを閉じます。再接続を実装するには[再接続のルール](https://docs.tiro.ooo/ja/developers/realtime-transcription/protocol.md#reconnect)に従ってください。

> [accordion] stream\_pcm.py の全コードを見る
>
> ```python stream_pcm.py theme={"system"}
> import json
> import os
> import ssl
> import sys
> import time
> import urllib.request
>
> import websocket
>
> BASE_URL = "https://api.tiro.ooo"
> API_KEY = os.environ["TIRO_API_KEY"]
> FRAME_BYTES = 3200
> FRAME_SECONDS = 0.1
>
>
> def rest(method, path, body=None):
>     request = urllib.request.Request(
>         f"{BASE_URL}{path}",
>         data=json.dumps(body).encode() if body is not None else None,
>         headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
>         method=method,
>     )
>     with urllib.request.urlopen(request) as response:
>         return json.load(response)
>
>
> def handle(message):
>     kind = message.get("type")
>     if kind in ("transcript", "translation"):
>         marker = "FINAL  " if message["isFinal"] else "partial"
>         print(f"{marker} {kind} ({message.get('locale')}) {message['text']}")
>     elif kind == "error":
>         print(f"error reason={message['reason']} retryable={message['retryable']} {message['message']}")
>         if message["fatal"]:
>             raise ConnectionError(message["reason"])
>     elif kind == "session.ended":
>         print(f"session.ended endReason={message['endReason']} audioDurationMs={message['audioDurationMs']}")
>
>
> def recv_json(ws):
>     raw = ws.recv()
>     if not raw:
>         raise ConnectionError("connection closed")
>     return json.loads(raw)
>
>
> def drain(ws):
>     ws.settimeout(0.0)
>     try:
>         while True:
>             handle(recv_json(ws))
>     except (websocket.WebSocketTimeoutException, BlockingIOError, ssl.SSLWantReadError):
>         pass
>     finally:
>         ws.settimeout(None)
>
>
> def stream(session, pcm):
>     ws = websocket.create_connection(
>         session["wsUrl"],
>         header=[f"Authorization: Bearer {session['streamToken']}"],
>         suppress_origin=True,
>     )
>     started_at = time.monotonic()
>     sent_seconds = 0.0
>     for offset in range(0, len(pcm), FRAME_BYTES):
>         ws.send_binary(pcm[offset : offset + FRAME_BYTES])
>         sent_seconds += FRAME_SECONDS
>         drain(ws)
>         wait = started_at + sent_seconds - time.monotonic()
>         if wait > 0:
>             time.sleep(wait)
>
>     ws.send(json.dumps({"type": "session.end"}))
>     ws.settimeout(60)
>     while True:
>         message = recv_json(ws)
>         handle(message)
>         if message.get("type") == "session.ended":
>             break
>     ws.close()
>
>
> def main():
>     with open(sys.argv[1], "rb") as file:
>         pcm = file.read()
>     session = rest("POST", "/v1/external/realtime-transcriptions", {"transcriptionLocale": "ko_KR", "translationLocale": "en_US"})
>     print(f"created id={session['id']}")
>     try:
>         stream(session, pcm)
>     except (websocket.WebSocketException, ConnectionError, OSError) as error:
>         print(f"websocket failed: {error}. Ending the session over REST.")
>         rest("POST", f"/v1/external/realtime-transcriptions/{session['id']}/end")
>         sys.exit(1)
>
>
> if __name__ == "__main__":
>     main()
> ```

PCMファイルのパスを引数に渡して実行してください。

```bash theme={"system"}
python stream_pcm.py meeting.pcm
```

## 次のステップ

> [card] [WebSocketプロトコル](https://docs.tiro.ooo/ja/developers/realtime-transcription/protocol.md)
>
> メッセージのフィールド、エラー、再接続、close codeを確認できます。

> [card] [APIリファレンス](https://docs.tiro.ooo/api-reference/realtime-transcription/create-realtime-transcription.md)
>
> セッションの作成、取得、一覧、終了のREST APIを確認できます。

Enterprise Plan

この機能はエンタープライズプランでご利用いただけます。  組織に必要なセキュリティ構成を営業チームがご支援します。

[導入のお問い合わせ](https://tiro.ax/ja/contact?utm_source=docs\&utm_medium=plan_callout\&utm_campaign=plan_upsell_enterprise\&utm_content=developers-realtime-transcription-quickstart)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.