# 文字起こし Webhookで、完了・失敗を署名付きで受け取れます

> 文字起こし Webhookは、完了・失敗を署名付きPOSTで通知します。作成時にwebhook_urlを指定すれば、完了を何度も問い合わせずに次の処理へ進めます。

Canonical: https://transcript1.com/ja/features/mojiokoshi-webhook
Language: ja
Last updated: 2026-10-19

文字起こし Webhookを設定すると、完了・失敗の通知を署名付きPOSTで受け取れます。作成時にwebhook_urlを指定するだけです。アプリはステータスを繰り返し問い合わせず、通知を受けて次の処理に進めます。

- transcription.completed・transcription.failedを通知
- 配信ごとにHMAC-SHA256で署名
- 待ち時間を延ばしながら最大6回配信

## 通知を受け取り、完了した文字起こしをIDで取得します

### 完了と失敗を通知します

イベントはtranscription.completedとtranscription.failedです。文字起こしをキャンセルした場合はWebhookを送りません。

### 署名で送信元を検証できます

X-Transcript1-SignatureにタイムスタンプとHMAC-SHA256署名を付けます。チームのwhsec_で始まるシークレットを使い、生のリクエスト本文を検証します。

### 受信できなければ再送します

2xxの応答で配信完了とします。それ以外は2、4、8、16、32分の間隔で再試行し、初回を含め最大6回送ります。

### 公開されたHTTPSのURLを指定します

URLは公開アドレスに解決できる必要があります。文字起こしの作成時と、毎回の配信時に送信先を確認します。

### 本文は通知後に取得します

通知には文字起こしオブジェクトが入りますが、文字起こしテキストは含みません。IDを使い、必要な形式でAPIから取得します。

## webhook\_urlに受信用のURLを渡します

動画リンクのリクエスト、分割アップロードの予約、1回で送るアップロードのフォームフィールドで指定できます。reuse_recentとは併用できません。[設定方法](https://transcript1.com/ja/docs/webhooks#set-a-webhook-url)を確認してください。

インタビューのアップロード、番組アーカイブの作成、複数のSNS動画の処理など、最初のリクエストが終わっても続く作業に使えます。作成時の文字起こしIDをアプリ側のジョブと一緒に保存し、後で届く通知を対応付けます。

受信先には、リダイレクトしない最終的な公開HTTPS URLを指定します。localhostには配信できません。以下は自分の録音をアップロードして通知を試す例です。

### 完了通知を指定して録音を送る例

```
curl -X POST "https://api.transcript1.com/v1/transcriptions" \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -F file=@episode.mp3 \
  -F webhook_url=https://example.com/hooks/transcript1
```

## 重複を確認してから、次の処理を予約します

dataには文字起こしテキストを除いたオブジェクトが入ります。data.idとtypeを組み合わせ、同じ通知を重複処理しないようにします。完了した文字起こしは[取得API](https://transcript1.com/ja/docs/api-reference#get-a-transcription)で読み出します。

処理済みでも、応答が10秒のタイムアウトに間に合わなければ再送される場合があります。記事の公開やユーザー通知の前に、文字起こしIDとイベント種別を重複確認用に保存します。署名を検証したら、イベントを永続化するかキューに入れ、速やかに2xxを返してください。

文字起こしテキストの取得はdata.idを使ってバックグラウンドで行います。transcription.failedならdata.errorを調べ、アプリ側の状態を更新します。キャンセル時には通知が来ないため、キャンセルした側の処理で状態を更新します。

### 受信URLに届くPOSTの例

```
X-Transcript1-Signature: t=<unix seconds>,v1=<hex digest>

{
  "type": "transcription.completed",
  "created_at": "2026-10-13T09:30:04.000Z",
  "data": {
    "id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
    "object": "transcription",
    "status": "completed",
    "transcript_source": "speech_to_text",
    "billed_minutes": 4,
    "error": null
  }
}
```

## JSONを読む前にHMAC-SHA256を検証します

t、ピリオド、生の本文を順につないだ値に、チームのwhsec_シークレットでHMAC-SHA256を計算します。署名を定数時間で比較し、古すぎるタイムスタンプも拒否します。[署名検証の手順](https://transcript1.com/ja/docs/webhooks#verify-the-signature)に詳しい実装例があります。

JSONパーサーが触る前の、受信したままの本文を使います。JSONを読み込んでから再シリアライズすると、空白やキーの順序が変わり検証できなくなる場合があります。ヘッダーとタイムスタンプを検証し、同じ長さのダイジェストを比較してから本文を読みます。

以下の短い例は、tとv1の抽出・検証が済んでいる前提です。ドキュメントにはタイムスタンプの許容差を5分にしたNode.jsとPythonの例があります。シークレットはwhsec_を含む全体を使います。ローテーション後の配信には、すぐに新しいシークレットが使われます。

### Node.js

```
const expected = crypto
  .createHmac('sha256', secret)
  .update(`${t}.${rawBody}`)
  .digest('hex');
const valid = v1.length === expected.length
  && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
```

- [署名検証の実装例を確認する](https://transcript1.com/ja/docs/webhooks)

## 正常系だけでなく、失敗と再送もテストします

完了、失敗、重複した通知を受信ハンドラーに送って確認します。同じ完了通知で2回公開しないこと、失敗時に存在しない文字起こしを公開しようとしないことを確かめます。署名の拒否と、アプリ側の処理エラーも別々にテストします。

配信のタイムアウトは10秒です。検索インデックスの更新やAIの処理を待たずに応答します。応答後の作業を再試行するキューは、アプリ側で用意してください。Webhook自体の再送は、その代わりにはなりません。

2xx以外の応答、接続エラー、タイムアウトでは、2、4、8、16、32分後に再試行します。最大6回で、リダイレクトには追従しません。ログにはジョブID、イベント種別、配信状況を残します。署名用のシークレットは記録しないでください。

再試行を使い切った場合は、保存してあるIDを[取得API](https://transcript1.com/ja/docs/api-reference#get-a-transcription)で確認します。Webhookだけをジョブの存在記録にせず、作成時からIDを保持すれば、録音を再送せずに通知漏れを調べられます。

## よくある質問

### 署名用のシークレットはどこにありますか？

ダッシュボードの「Webhooks」で確認できます。whsec_で始まるチーム共通のシークレットです。同じ画面でいつでもローテーションできます。

### 受信に失敗した場合はどうなりますか？

待ち時間を延ばしながら、約1時間にわたり初回を含め最大6回送ります。1回のタイムアウトは10秒です。リダイレクトには追従しません。

### MCPからもWebhookを設定できますか？

いいえ。WebhookはREST APIのみです。MCPではツールが完了を待つか、返されたIDで後から文字起こしを取得します。

## あわせて読む

- [Webhookで完了を通知する （ドキュメント）](https://transcript1.com/ja/docs/webhooks)
- [音声・動画のAPI](https://transcript1.com/ja/features/onsei-ninshiki-api)
- [AIエージェント・LLM](https://transcript1.com/ja/use-cases/ai-ejento-mojiokoshi)
