> ## 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.

# 개요

> Voice File Job 생성, 업로드, 상태 확인, 결과 조회·삭제 방법.

export const PlanGateCard = ({plan, page, lang = "ko", children}) => {
  const BADGE = {
    pro: {
      ko: "Pro+",
      en: "Pro+",
      ja: "Pro+"
    },
    team: {
      ko: "팀 플랜",
      en: "Team Plan",
      ja: "チームプラン"
    },
    enterprise: {
      ko: "엔터프라이즈",
      en: "Enterprise",
      ja: "エンタープライズ"
    }
  };
  const COPY = {
    pro: {
      ko: {
        lead: "이 기능은 Pro 플랜 이상에 포함되어 있어요.",
        tail: "지금 쓰는 워크스페이스에서 바로 업그레이드할 수 있어요.",
        cta: "플랜 업그레이드하기"
      },
      en: {
        lead: "This feature is included in Pro plans and above.",
        tail: "You can upgrade right from your workspace.",
        cta: "Upgrade your plan"
      },
      ja: {
        lead: "この機能はProプラン以上でご利用いただけます。",
        tail: "ワークスペースからすぐにアップグレードできます。",
        cta: "プランをアップグレード"
      }
    },
    team: {
      ko: {
        lead: "이 기능은 함께 쓰는 워크스페이스에서 진가가 나와요.",
        tail: "팀원을 초대하면 바로 시작할 수 있어요.",
        cta: "팀원 초대하기"
      },
      en: {
        lead: "This feature shines in a shared workspace.",
        tail: "Invite your teammates to get started.",
        cta: "Invite teammates"
      },
      ja: {
        lead: "この機能はチームで使うワークスペースで真価を発揮します。",
        tail: "メンバーを招待してすぐに始められます。",
        cta: "メンバーを招待"
      }
    },
    enterprise: {
      ko: {
        lead: "이 기능은 엔터프라이즈 플랜에서 제공돼요.",
        tail: "조직에 필요한 보안 구성을 영업팀이 함께 설계해 드려요.",
        cta: "도입 문의하기"
      },
      en: {
        lead: "This feature is available on the Enterprise plan.",
        tail: "Our sales team will help design the security setup your organization needs.",
        cta: "Contact sales"
      },
      ja: {
        lead: "この機能はエンタープライズプランでご利用いただけます。",
        tail: "組織に必要なセキュリティ構成を営業チームがご支援します。",
        cta: "導入のお問い合わせ"
      }
    }
  };
  const HREF = {
    pro: "https://tiro.ooo/" + lang + "/n?utm_source=docs&utm_medium=plan_callout&utm_campaign=plan_upsell_pro&utm_content=" + page + "#settings/plans",
    team: "https://tiro.ooo/" + lang + "/n?utm_source=docs&utm_medium=plan_callout&utm_campaign=plan_upsell_team&utm_content=" + page + "#settings/plans",
    enterprise: "https://tiro.ax/" + lang + "/contact?utm_source=docs&utm_medium=plan_callout&utm_campaign=plan_upsell_enterprise&utm_content=" + page
  };
  const copy = COPY[plan][lang];
  return <div className="plan-gate-card">
      <div className="plan-gate-card-text">
        <span className="plan-gate-card-title">
          {({
    pro: "Pro Plan",
    team: "Team Plan",
    enterprise: "Enterprise Plan"
  })[plan]}
        </span>
        <p>
          {copy.lead} {children} {copy.tail}
        </p>
      </div>
      <a className="plan-gate-card-button" href={HREF[plan]}>
        {copy.cta}
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
      </a>
    </div>;
};

export const PlanBadge = ({plan, lang = "ko"}) => <span className="plan-badge">
    {({
  pro: {
    ko: "Pro+",
    en: "Pro+",
    ja: "Pro+"
  },
  team: {
    ko: "팀 플랜",
    en: "Team Plan",
    ja: "チームプラン"
  },
  enterprise: {
    ko: "엔터프라이즈",
    en: "Enterprise",
    ja: "エンタープライズ"
  }
})[plan][lang]}
  </span>;

<PlanBadge plan="enterprise" lang="ko" /> Voice File Job API는 엔터프라이즈 플랜에서 제공하는 기능이에요. 개인 플랜과 팀 플랜에서는 활성화할 수 없어요.

Voice File Job은 생성 → 업로드 → upload-complete 호출 → 상태 확인 → 결과 조회 순서로 처리해요. upload-complete는 클라이언트가 직접 호출하며 S3 이벤트는 사용하지 않아요.

## 필요한 것

* 계정 API Key 또는 워크스페이스 API Key: `Authorization: Bearer {id}.{secret}`
* scope를 지정한 키라면 작업 생성·업로드 완료·삭제에는 `voice_file_job:write`, 조회에는 `voice_file_job:read`가 필요해요. `write`는 `read`를 포함하고, scope를 지정하지 않은 키는 모든 작업을 할 수 있어요.
* 엔터프라이즈 플랜 계약과 함께 Voice File Job 정책이 활성화된 워크스페이스가 필요해요.
* 워크스페이스에 묶이지 않은 계정 API Key와 조직 API Key는 작업을 만들 때 `workspaceGuid`를 보내야 해요.
* transcriptLocaleHints: 최대 1개 (선택)
  * **제공하지 않으면 언어가 자동으로 감지돼요**
* translationLocales: 최대 5개 (선택)

## 전체 체크리스트

1. Job 생성 → `{ id, uploadUri }` 수신
2. `uploadUri`에 오디오 업로드
3. `PUT /v1/external/voice-file/jobs/{jobId}/upload-complete` 호출
4. `voice_file_job.completed` 또는 `voice_file_job.failed` 웹훅으로 완료 받기 (권장). 웹훅을 받을 수 없으면 job 상태 폴링
5. transcript 및 (선택적으로) translations 가져오기
6. 콘텐츠를 더 잘 이해하기 위해 paragraph summary 가져오기

## 작업 완료는 어떻게 확인하나요? 웹훅 (권장)

작업 완료는 웹훅으로 받으세요. Tiro가 처리를 끝내면 `voice_file_job.completed` 또는 `voice_file_job.failed` 이벤트를 보내요.

처리에는 파일 길이에 따라 보통 45초\~18분이 걸리고, 대기열 상황에 따라 더 길어질 수 있어요. 웹훅을 구독하면 그동안 상태를 반복해서 조회하지 않아도 돼요.

* `voice_file_job.completed`와 `voice_file_job.failed`를 구독하세요. 워크스페이스 Webhook은 그 워크스페이스에서 만든 모든 작업의 이벤트를 받고, 조직 Webhook은 조직에 속한 모든 워크스페이스에서 만든 작업의 이벤트를 받아요. 노트와 달리 폴더 공유 범위는 적용되지 않아요. 계정 Webhook은 본인이 계정 API Key로 만든 작업 중 Webhook을 등록한 워크스페이스의 작업 이벤트만 받아요. 등록 방법은 [Webhook 개요](/ko/developers/webhooks/webhooks-overview)를 참고하세요.
* job ID는 이벤트의 `data.resourceId`에, 결과는 `data.resource.status`에 `COMPLETED` 또는 `FAILED`로 들어 있어요. 전체 필드는 [Event Structure](/ko/developers/webhooks/events/schema)에서 확인하세요.
* 웹훅 페이로드에는 작업 메타데이터만 있어요. 전사 본문은 `GET /v1/external/voice-file/jobs/{jobId}/transcript`로 가져오세요.
* Tiro는 재시도까지 모두 실패한 이벤트를 다시 보내지 않아요. 예상 처리 시간이 지났는데 이벤트가 없으면 `GET /v1/external/voice-file/jobs/{jobId}`로 상태를 확인하세요.

작업 생성과 삭제를 추적하려면 `voice_file_job.created`와 `voice_file_job.deleted`도 구독하세요.

## 폴링 전략 (웹훅을 받을 수 없을 때)

웹훅 endpoint를 운영할 수 없거나 웹훅 이벤트가 오지 않을 때는 job 상태를 폴링하세요.

* `GET /v1/external/voice-file/jobs/{jobId}`를 exponential backoff로 폴링해요
  * 1\~2s 간격으로 시작해서 4s, 8s, 최대 30s까지 늘려요
  * 중단 조건:
    * 성공: `status` = `COMPLETED`
    * 실패: `status` = `FAILED`
  * 성공 후:
    * 항상 transcript를 가져와요: `GET /v1/external/voice-file/jobs/{jobId}/transcript`
    * translations를 요청했다면: `GET /v1/external/voice-file/jobs/{jobId}/translations` 또는 locale별 endpoint
    * paragraph summary 가져오기:
      * transcript의 경우: `GET /v1/external/voice-file/jobs/{jobId}/transcript/paragraph-summary`
      * translations의 경우: `GET /v1/external/voice-file/jobs/{jobId}/translations/{locale}/paragraph-summary`

<Note>
  처리가 성공했을 때의 최종 상태는 `COMPLETED`예요. 이는 transcript, translation(요청한 경우), 그리고 둘 다에 대한 paragraph summary를 포함한 모든 처리가 완료되었다는 의미예요.
</Note>

## 작업이 FAILED여도 전사를 받을 수 있나요?

받을 수 있는 경우가 있어요. 요약 생성만 실패해도 작업은 `FAILED`가 돼요. 이때 `GET /v1/external/voice-file/jobs/{jobId}` 응답의 `transcriptAvailable`이 `true`이면 `GET /v1/external/voice-file/jobs/{jobId}/transcript`로 전사를 가져올 수 있어요.

실패 이유는 `errorMessage`에 있어요. `voice_file_job.failed` 웹훅의 `data.resource.errorMessage`에도 같은 값이 들어 있어요.

## 오디오와 전사 데이터는 어떻게 보관되나요?

Tiro는 업로드한 오디오를 객체 저장소에, 전사·번역 결과를 서비스 데이터베이스에 저장해요. API에는 자동 보관 기간을 설정하는 기능이 없으므로, 데이터가 더 필요하지 않으면 `DELETE /v1/external/voice-file/jobs/{jobId}`를 호출하세요.

어떤 상태의 작업이든 `DELETE /v1/external/voice-file/jobs/{jobId}`로 삭제할 수 있어요. Tiro는 오디오와 생성된 결과를 삭제하고, 삭제 뒤 도착하는 처리 콜백은 무시해요.

계정 API Key는 자신이 만든 작업만, 워크스페이스 API Key는 같은 워크스페이스의 모든 작업에 접근해요. 작업을 삭제하면 오디오와 전사·번역 결과도 지워져 다시 조회할 수 없어요.

## 파일 제한 및 요구사항

### 지원하는 오디오 포맷

지원하는 파일 형식은 다음과 같아요.

**오디오 포맷:**

| Format | MIME Type | Extension |
| - | - | - |
| **MP3** | audio/mpeg | .mp3 |
| **WAV** | audio/wav | .wav |
| **M4A** | audio/mp4 | .m4a |

**비디오 포맷 (오디오 추출):**

| Format | MIME Type | Extension |
| - | - | - |
| **MP4** | video/mp4 | .mp4 |

### 파일 크기 및 길이 제한

| 제한 종류 | 값 | 비고 |
| - | - | - |
| **최대 파일 크기** | 500 MB | |
| **최대 길이** | 4시간 | |
| **최소 샘플레이트** | 8 kHz | 음성 인식 최소 기준 |
| **권장 샘플레이트** | 16 kHz+ | |
| **최대 샘플레이트** | 48 kHz | |
| **채널** | Mono 또는 Stereo | 다중 화자 지원 |

### 처리 시간 예상치

파일 길이별 예상 처리 시간은 다음과 같아요.

| 파일 길이 | 일반적인 처리 시간 |
| - | - |
| 5분 미만 | 45-75초 |
| 5-20분 | 1-3분 |
| 20-60분 | 3-6분 |
| 1-4시간 | 6-18분 |

## Paragraph Summary 기능

Paragraph Summary는 전사 또는 번역 결과를 단락별로 요약해요.

### 동작 방식

전사 텍스트를 단락으로 나눈 뒤 각 단락의 요약을 만들어요. 번역을 요청했다면 번역 결과에도 같은 과정을 적용해요. 요약은 전사·번역 처리가 끝난 뒤 비동기로 생성돼요.

## 시퀀스

```mermaid theme={"system"}
sequenceDiagram
    participant Client
    participant API as Tiro API
    participant Storage as File Storage
    participant Worker as Tiro Worker
    participant STT as STT Engine
    participant TR as Translation Engine
    participant WH as Webhook Service

    %% Job creation
    Client->>API: POST /v1/external/voice-file/jobs (workspaceGuid, transcriptLocaleHints, translationLocales)
    API->>Storage: Generate upload URL
    API-->>Client: 201 Created {id, uploadUri}

    %% File upload
    Client->>Storage: PUT audio file
    Client->>API: PUT /v1/external/voice-file/jobs/{jobId}/upload-complete

    %% Pipeline
    API->>Worker: Enqueue job (start processing)
    Worker->>STT: Speech-to-text
    STT-->>Worker: transcript
    alt translations requested
        Worker->>TR: Translate to requested locales
        TR-->>Worker: translations
    end
    Worker->>API: mark job = COMPLETED or FAILED
    opt Webhook configured
        API-->>WH: POST voice_file_job.completed or voice_file_job.failed
    end

    %% Results
    Client->>API: GET /v1/external/voice-file/jobs/{jobId}
    API-->>Client: status, timestamps
    Client->>API: GET /v1/external/voice-file/jobs/{jobId}/transcript
    API-->>Client: transcript
    Client->>API: GET /v1/external/voice-file/jobs/{jobId}/translations
    API-->>Client: translations[]
```

전체 예시는 [단계별 튜토리얼](/ko/developers/voice-file/tutorial)을 참고하세요.

<PlanGateCard plan="enterprise" page="developers-voice-file-overview" lang="ko" />


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