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

# Note Events

> Note 리소스에 대한 webhook 이벤트

export const Json = ({data, lang = 'json', spaces = 2}) => <pre>
    <code className={`language-${lang}`}>{JSON.stringify(data, null, spaces)}</code>
  </pre>;

export const EventBaseStructure = {
  id: "evt_01J9ABCDEF",
  type: "note.created",
  createdAt: "2025-09-05T07:12:34Z",
  workspaceGuid: "ws_a1b2c3d4",
  data: {
    resourceType: "Note",
    resourceId: "note-guid-123",
    resource: {
      "title": "Meeting Note"
    }
  }
};

export const NoteBaseJson = {
  guid: 'note-guid-123',
  workspaceGuid: 'ws_a1b2c3d4',
  title: 'Meeting notes',
  createdAt: '2025-07-20T10:00:00Z',
  updatedAt: '2025-07-20T11:10:00Z',
  sourceType: 'live-voice',
  recordingStartAt: '2025-07-20T10:00:10Z',
  recordingEndAt: '2025-07-20T11:00:10Z',
  recordingDurationSeconds: 3600,
  transcribeLocale: 'en_US',
  translateLocale: 'ko_KR',
  webUrl: 'https://tiro.ooo/n/123',
  collaborators: [{
    guid: 'user-guid-123',
    name: 'John Doe',
    email: 'john@example.com',
    role: 'OWNER'
  }, {
    guid: 'user-guid-456',
    name: 'Jane Smith',
    email: 'jane@example.com',
    role: 'EDITOR'
  }, {
    guid: 'user-guid-789',
    name: 'Sam',
    email: 'sam@example.com',
    role: 'VIEWER'
  }],
  participants: [{
    name: 'Alice Kim',
    email: 'alice@example.com'
  }, {
    name: 'Bob Park',
    email: null
  }]
};

## 개요

Note 리소스에 대한 webhook 이벤트예요. 모든 note 이벤트는 `resourceType: "Note"`를 사용해요.

## 이벤트 상세

### note.created

* 실시간 음성 노트 녹음을 시작할 때
* 녹음 파일로부터 노트를 생성할 때

### note.deleted

* 사용자가 노트를 영구적으로 삭제할 때

### note.recording.completed (녹음 일시정지)

* 실시간 녹음을 일시정지하고, 그 시점까지의 오디오 처리와 텍스트 변환, 문단 생성이 끝났을 때 발생해요
* 한 회의에서 녹음을 여러 번 일시정지할 수 있으므로 같은 노트에서 여러 번 발생할 수 있어요
* 이벤트 이름에는 `completed`가 남아 있지만, 노트가 끝났다는 뜻은 아니에요. 노트의 최종 종료는 `note.ended`로 확인하세요

### note.user\_content.updated

* 사용자가 노트 내용을 직접 편집할 때

### note.ended

* 노트가 완전히 끝났을 때 (모든 처리가 완료된 후) 발생해요
* 여러 번 발생할 수 있는 `note.recording.completed`와 달리, 노트 수명 주기당 한 번만 발생해요
* 노트 완료를 확정하는 이벤트예요
* 노트가 최종 상태에 도달했다는 것을 알아야 할 때 이 이벤트를 사용하세요

### note.participants.updated

* 노트에서 회의 참가자가 추가되거나 제거될 때 발생해요
* 기존 참가자의 이름이나 이메일이 변경될 때는 발생하지 **않아요**
* `resource` 필드에는 업데이트된 `participants` 배열을 포함한 전체 Note 오브젝트가 담겨요

## Payload 예시

<Json
  data={{
...EventBaseStructure,
type: "note.ended",
data: {
...EventBaseStructure.data,
resource: NoteBaseJson
}
}}
/>

## 리소스 데이터

Note 이벤트에는 완전한 Note 리소스 데이터가 포함돼요. 필드 설명은 [Note API](/api-reference/note/get-note)를 참고하세요.

**중요**: Note 리소스에는 메타데이터만 담겨 있어요. transcript 내용은 [List Note Paragraphs](/api-reference/note/list-note-paragraphs) API를 사용해 실제 텍스트 데이터를 가져오세요.

## 가이드

### 실시간 녹음의 일시정지와 종료 구분하기

* `note.recording.completed`는 녹음 일시정지를 알려요. 이벤트를 받을 때마다 최신 노트 데이터를 가져오세요
* `note.ended`는 노트가 완전히 끝났을 때 한 번만 발생해요
* 두 이벤트를 함께 구독하면 일시정지 시점의 중간 결과와 최종 종료를 각각 처리할 수 있어요

**예시 시나리오:**

1. 회의 시작 → 녹음 시작
2. 녹음 일시정지 → transcript와 요약 처리 → `note.recording.completed` 발생
3. 녹음 재개 → 처리 계속
4. 녹음 일시정지 → `note.recording.completed` 다시 발생
5. 녹음 재개 후 종료 → `note.ended` 발생

**예시 처리:**

<CodeGroup>
  ```javascript Node.js theme={"system"}
  async function handleRecordingPaused(event) {
    const noteId = event.data.resourceId;
    const note = event.data.resource;
    const paragraphs = await fetchNoteParagraphs(noteId);

    processPausedNote(note, paragraphs);
  }
  ```

  ```python Python theme={"system"}
  async def handle_recording_paused(event):
      note_id = event['data']['resourceId']
      note = event['data']['resource']
      paragraphs = await fetch_note_paragraphs(note_id)

      process_paused_note(note, paragraphs)
  ```

  ```go Go theme={"system"}
  func handleRecordingPaused(event WebhookEvent) error {
      noteID := event.Data.ResourceID
      note := event.Data.Resource

      paragraphs, err := fetchNoteParagraphs(noteID)
      if err != nil {
          return err
      }

      return processPausedNote(note, paragraphs)
  }
  ```

  ```kotlin Kotlin + Spring theme={"system"}
  suspend fun handleRecordingPaused(event: WebhookEvent) {
      val noteId = event.data.resourceId
      val note = event.data.resource
      val paragraphs = fetchNoteParagraphs(noteId)

      processPausedNote(note, paragraphs)
  }
  ```
</CodeGroup>
