> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# 비동기 텍스트 음성 변환

> **Note**
>
> **사용 가이드** · [텍스트 음성 변환 퀵스타트](/docs/ko/eleven-api/guides/cookbooks/speech-to-text)를 완료했다고 가정합니다.

## 개요

웹훅을 사용하면 텍스트 음성 변환 전사 작업이 완료될 때 자동 알림을 받을 수 있어, 상태 업데이트를 위해 API를 지속적으로 폴링할 필요가 없습니다. 특히 장시간 실행되는 전사 작업이나 대량의 오디오 파일을 처리할 때 유용합니다.

전사가 완료되면 ElevenLabs는 전사 텍스트, 언어 감지 및 메타데이터를 포함한 전사 결과를 지정한 웹훅 URL로 POST 요청으로 전송합니다.

## 웹훅 사용하기

> **Note**
>
> 이 가이드는 [API 키와 SDK를 설정](/docs/ko/eleven-api/quickstart)했다고 가정합니다. 아직 완료하지 않았다면
> 먼저 퀵스타트를 완료하세요.

#### 웹훅 만들기 또는 수정하기

ElevenLabs 대시보드에서
[**개발자** > **웹훅**](https://elevenlabs.io/app/developers/webhooks)으로 이동합니다.
**웹훅 만들기**를 클릭하거나 기존 웹훅을 수정합니다.

![Transcription completed가 선택된 웹훅 만들기 대화 상자](/docs/_fern-files/_img/866f389106e74e16586a4d68cf8160f2d27568518ff4f984c2fab81cbd1674c9.webp)

다음과 같이 웹훅을 구성합니다.

* **이름**: 웹훅을 설명하는 이름
* **콜백 URL**: 공개적으로 접근 가능한 HTTPS 엔드포인트
* **웹훅 인증 방식**: `HMAC` 또는 `OAuth` 중 하나입니다. 검증 메커니즘은 클라이언트에서 구현해야 합니다. ElevenLabs는 검증에 사용할 수 있는 헤더를 전송하지만 이를 강제하지는 않습니다.
* **이벤트**: **Transcription completed**를 선택합니다.

#### 웹훅 매개변수를 활성화하여 API 호출하기

텍스트 음성 변환 API 호출 시 `webhook` 매개변수를 `true`로 설정하면 해당 요청에 대한 웹훅 알림이 활성화됩니다.

```python maxLines=0
from dotenv import load_dotenv
from elevenlabs.client import ElevenLabs

load_dotenv()

elevenlabs = ElevenLabs(
  api_key=os.getenv("ELEVENLABS_API_KEY"),
)

def transcribe_with_webhook(audio_file):
  try:
    result = elevenlabs.speech_to_text.convert(
      file=audio_file,
      model_id="scribe_v2",
      webhook=True,
    )
    print(f"Transcription started: {result.request_id}")
    return result
  except Exception as e:
    print(f"Error starting transcription: {e}")
    raise e
```

```typescript maxLines=0
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

async function transcribeWithWebhook(audioFile) {
  try {
    const result = await elevenlabs.speechToText.convert({
      file: audioFile,
      modelId: 'scribe_v2',
      webhook: true,
    });

    console.log('Transcription started:', result.requestId);
    return result;
  } catch (error) {
    console.error('Error starting transcription:', error);
    throw error;
  }
}
```

## 웹훅 페이로드

전사가 완료되면 웹훅 엔드포인트는 전사 및 웹훅 데이터가 포함된 POST 요청을 받습니다.

```json
{
  type: 'speech_to_text_transcription',
  data: {
    request_id: 'some-request-id-123',
    webhook_metadata: { ... }, // if provided in the convert request
    transcription: {
      "language_code": "en",
      "language_probability": 0.98,
      "text": "Hello world!",
      "words": [
        {
          "text": "Hello",
          "start": 0.0,
          "end": 0.5,
          "type": "word",
          "speaker_id": "speaker_1"
        },
        {
          "text": " ",
          "start": 0.5,
          "end": 0.5,
          "type": "spacing",
          "speaker_id": "speaker_1"
        },
        {
          "text": "world!",
          "start": 0.5,
          "end": 1.2,
          "type": "word",
          "speaker_id": "speaker_1"
        }
      ]
    }
  }
}
```

응답 구조에 관한 자세한 내용은 [텍스트 음성 변환 API](/docs/ko/api-reference/speech-to-text/v-1-speech-to-text-realtime) 레퍼런스를 참고하세요.

요청에 [`transcript_edit`](/docs/ko/eleven-api/guides/how-to/speech-to-text/batch/transcript-editing) 지침이 포함된 경우, `transcription` 객체에는 편집된 텍스트가 담긴 `edited_transcript` 필드도 포함됩니다.

전사에 실패하면 엔드포인트는 대신 `webhook_error` 이벤트를 받습니다. 이 이벤트에는 `data` 객체가 없습니다. 최상위 수준의 `request_id`(요청이 수락되었을 때 반환된 동일한 ID), `error_message`, 요청의 `webhook_metadata`를 포함합니다.

## 웹훅 엔드포인트 구현하기

수신 알림을 처리하는 웹훅 엔드포인트 구현 예시는 다음과 같습니다.

```javascript
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
import express from "express";

const elevenlabs = new ElevenLabsClient();

const app = express();

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// The raw body is required: verification runs over the exact bytes sent.
app.post("/webhook/speech-to-text", express.raw({ type: "application/json" }), async (req, res) => {
  const signature = req.headers["elevenlabs-signature"];

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(
      req.body.toString(),
      signature,
      WEBHOOK_SECRET
    );
  } catch (error) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  if (event.type === "speech_to_text_transcription") {
    const { request_id, transcription } = event.data;

    console.log(`Transcription ${request_id} completed`);
    console.log(`Language: ${transcription.language_code}`);
    console.log(`Text: ${transcription.text}`);

    processTranscription(request_id, transcription);
  } else if (event.type === "webhook_error") {
    console.error(`Transcription ${event.request_id} failed: ${event.error_message}`);
    handleTranscriptionError(event.request_id, event.webhook_metadata, event.error_message);
  }

  res.status(200).json({ received: true });
});

async function processTranscription(requestId, transcription) {
  console.log("Processing completed transcription...");
}

async function handleTranscriptionError(requestId, webhookMetadata, errorMessage) {
  console.log("Handling transcription error...");
}

app.listen(3000, () => {
  console.log("Webhook server listening on port 3000");
});
```

## 보안 고려 사항

### 서명 검증

요청이 ElevenLabs에서 온 것인지 확인하기 위해 항상 웹훅 서명을 검증하세요.

### HTTPS 요구 사항

전사 데이터를 안전하게 전송하려면 웹훅 URL에 HTTPS를 사용해야 합니다.

### 속도 제한

악용을 방지하기 위해 웹훅 엔드포인트에 속도 제한을 구현하세요.

```javascript
import rateLimit from "express-rate-limit";

const webhookLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
  message: "Too many webhook requests from this IP",
});

app.use("/webhook", webhookLimiter);
```

### 실패 응답

웹훅을 확인했음을 알리려면 `2xx` 상태 코드를 반환하세요.

기본적으로 텍스트 음성 변환 전사 웹훅은 한 번만 전송됩니다. `2xx`가 아닌 모든 응답은 전송 실패로 처리되며, 반복적으로 실패하면 [웹훅이 자동 비활성화됩니다](/docs/ko/eleven-api/resources/webhooks#auto-disable-behavior).

웹훅에 재시도를 켜면 실패한 전송(`5xx`, `408`, `429` 또는 연결 오류)은 이전 시도 후 약 30초, 2분, 8분, 30분, 30분 간격으로 최대 5회 더 재시도됩니다. 그 외 `4xx` 응답은 재시도되지 않습니다. 동일 이벤트에 대한 모든 시도에는 동일한 `ElevenLabs-Webhook-Delivery-Id` 헤더가 포함되므로, 핸들러에서 중복을 무시할 수 있습니다.

재시도가 켜진 경우 `enable_logging`을 `false`로 설정한 요청(보존 기간 0 모드)은 이전 시도 후 5초와 30초에 최대 2회의 빠른 재시도를 수행합니다. 전사본은 저장되지 않습니다.

전송이 계속 실패하면 요청이 수락되었을 때 반환된 `transcription_id`를 사용하여 [전사본 가져오기](/docs/ko/api-reference/speech-to-text/get)로 결과를 가져오세요. `enable_logging`을 `false`로 설정한 요청의 전사본은 저장되지 않습니다.

## 웹훅 테스트하기

### 로컬 개발

로컬에서 테스트하려면 [ngrok](https://ngrok.com/)과 같은 도구를 사용해 로컬 서버를 외부에 노출하세요.

```bash
ngrok http 3000
```

개발 중에는 제공된 HTTPS URL을 웹훅 엔드포인트로 사용하세요.

### 웹훅 테스트

전사 요청을 보내고 엔드포인트를 모니터링하여 웹훅 구현을 테스트할 수 있습니다.

```javascript
async function testWebhook() {
  const audioFile = new File([audioBuffer], "test.mp3", { type: "audio/mp3" });

  const result = await elevenlabs.speechToText.convert({
    file: audioFile,
    modelId: "scribe_v2",
    webhook: true,
  });

  console.log("Test transcription started:", result.requestId);
}
```

## 다음 단계

#### [서버 측 스트리밍](/docs/ko/eleven-api/guides/how-to/speech-to-text/realtime/server-side-streaming)

WebSocket 기반 스트리밍 API를 사용해 오디오를 실시간으로 전사합니다.

#### [API 레퍼런스](/docs/ko/api-reference/speech-to-text)

전체 텍스트 음성 변환 API 레퍼런스 및 매개변수입니다.