# Voice Notes API

The Voice Notes API manages audio recordings and their transcriptions. Voice notes are stored with metadata, tags, and workspace associations. Transcription is powered by Whisper and runs asynchronously -- trigger it on demand and poll for results. Deleted notes are soft-deleted (deleted_at timestamp) and filtered from all queries.

> **Base path:** `/api/v2/voice-notes` | **Auth:** JWT | **Rate limit:** 100 req/min

## POST /api/v2/voice-notes

Create Voice Note -- Creates a new voice note record. Accepts a URL to an already-uploaded audio file. If no title is provided, defaults to "Voice Note {datetime}". The note is created with status "created" -- transcription must be triggered separately.

**Authentication required**

### Body Parameters

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| audioUrl | string | Yes | -- | URL to the audio file (must be pre-uploaded). |
| title | string | No | Auto-generated | Title for the voice note. Auto-generated as "Voice Note {datetime}" if omitted. |
| duration | number | No | 0 | Recording duration in seconds. |
| mimeType | string | No | audio/webm | Audio MIME type. |
| metadata | object | No | {} | Arbitrary key-value metadata. |
| tags | string[] | No | [] | Array of tags for organization and filtering. |
| workspaceId | string (UUID) | No | -- | Associate the note with a specific workspace. |

### Example Request

```bash
curl -X POST https://api.lvng.ai/api/v2/voice-notes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sprint retro thoughts",
    "audioUrl": "https://cdn.lvng.ai/uploads/audio/a1b2c3d4.webm",
    "duration": 45,
    "mimeType": "audio/webm",
    "tags": ["retro", "sprint-14"],
    "workspaceId": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90"
  }'
```

### Response (201)

```json
{
  "success": true,
  "voiceNote": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "user_id": "8f3a2b1c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
    "workspace_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
    "title": "Sprint retro thoughts",
    "audio_url": "https://cdn.lvng.ai/uploads/audio/a1b2c3d4.webm",
    "duration": 45,
    "mime_type": "audio/webm",
    "metadata": {},
    "tags": ["retro", "sprint-14"],
    "status": "created",
    "created_at": "2026-03-19T10:00:00.000Z",
    "updated_at": "2026-03-19T10:00:00.000Z"
  }
}
```

## GET /api/v2/voice-notes

List Voice Notes -- Returns a paginated list of voice notes for the authenticated user. Soft-deleted notes (where deleted_at is set) are excluded. Results are ordered by created_at descending.

**Authentication required**

### Query Parameters

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| search | string | No | -- | Filter by title (case-insensitive partial match). |
| tags | string | No | -- | Comma-separated tags to filter by. Matches notes containing all specified tags. |
| status | string | No | -- | Filter by status: created, processing, completed, or failed. |
| workspaceId | string | No | -- | Filter by workspace ID. |
| limit | number | No | 50 | Maximum results to return. |
| offset | number | No | 0 | Number of results to skip for pagination. |

### Example Request

```bash
curl -X GET "https://api.lvng.ai/api/v2/voice-notes?tags=retro&status=completed&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200)

```json
{
  "success": true,
  "voiceNotes": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "user_id": "8f3a2b1c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
      "workspace_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
      "title": "Sprint retro thoughts",
      "audio_url": "https://cdn.lvng.ai/uploads/audio/a1b2c3d4.webm",
      "duration": 45,
      "mime_type": "audio/webm",
      "metadata": {},
      "tags": ["retro", "sprint-14"],
      "status": "completed",
      "created_at": "2026-03-19T10:00:00.000Z",
      "updated_at": "2026-03-19T10:00:45.000Z"
    }
  ],
  "total": 28,
  "limit": 10,
  "offset": 0
}
```

## GET /api/v2/voice-notes/:id

Get Voice Note -- Returns a single voice note by ID. Only returns notes owned by the authenticated user that have not been soft-deleted.

**Authentication required**

### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| id | string (UUID) | Yes | The unique identifier of the voice note. |

### Example Request

```bash
curl -X GET https://api.lvng.ai/api/v2/voice-notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200)

```json
{
  "success": true,
  "voiceNote": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "user_id": "8f3a2b1c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
    "workspace_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
    "title": "Sprint retro thoughts",
    "audio_url": "https://cdn.lvng.ai/uploads/audio/a1b2c3d4.webm",
    "duration": 45,
    "mime_type": "audio/webm",
    "metadata": {},
    "tags": ["retro", "sprint-14"],
    "status": "completed",
    "transcript": "I think the sprint went well overall. The main blocker was the calendar integration taking longer than expected...",
    "transcript_segments": [
      { "start": 0.0, "end": 3.2, "text": "I think the sprint went well overall." },
      { "start": 3.5, "end": 8.1, "text": "The main blocker was the calendar integration taking longer than expected." }
    ],
    "created_at": "2026-03-19T10:00:00.000Z",
    "updated_at": "2026-03-19T10:00:45.000Z"
  }
}
```

## PUT /api/v2/voice-notes/:id

Update Voice Note -- Updates a voice note's metadata. Only title, tags, and metadata fields can be modified -- audio data and transcripts cannot be changed via this endpoint. Returns 400 if no valid fields are provided.

**Authentication required**

### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| id | string (UUID) | Yes | The unique identifier of the voice note. |

### Body Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| title | string | No | Updated title for the voice note. |
| tags | string[] | No | Updated tags array (replaces existing tags). |
| metadata | object | No | Updated metadata object (replaces existing metadata). |

### Example Request

```bash
curl -X PUT https://api.lvng.ai/api/v2/voice-notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sprint 14 Retro - Key Takeaways",
    "tags": ["retro", "sprint-14", "action-items"]
  }'
```

### Response (200)

```json
{
  "success": true,
  "voiceNote": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "title": "Sprint 14 Retro - Key Takeaways",
    "tags": ["retro", "sprint-14", "action-items"],
    "status": "completed",
    "created_at": "2026-03-19T10:00:00.000Z",
    "updated_at": "2026-03-19T10:15:00.000Z"
  }
}
```

## DELETE /api/v2/voice-notes/:id

Delete Voice Note -- Soft-deletes a voice note by setting the deleted_at timestamp. The note will no longer appear in list or get queries but the data is preserved in the database.

**Authentication required**

### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| id | string (UUID) | Yes | The unique identifier of the voice note to delete. |

### Example Request

```bash
curl -X DELETE https://api.lvng.ai/api/v2/voice-notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200)

```json
{
  "success": true,
  "message": "Voice note deleted"
}
```

## POST /api/v2/voice-notes/:id/transcribe

Transcribe Voice Note -- Triggers Whisper transcription for a voice note. The status is set to "processing" immediately and updated to "completed" or "failed" when transcription finishes. The transcript and segment timestamps are stored on the voice note record.

**Authentication required**

### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| id | string (UUID) | Yes | The unique identifier of the voice note to transcribe. |

### Example Request

```bash
curl -X POST https://api.lvng.ai/api/v2/voice-notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890/transcribe \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200)

```json
{
  "success": true,
  "message": "Transcription queued",
  "status": "processing",
  "voiceNoteId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

## GET /api/v2/voice-notes/:id/transcript

Get Transcript -- Returns the transcript and segment timestamps for a voice note. Returns 404 if the transcript is not yet available (status is not "completed"). Segments include start/end timestamps in seconds and the corresponding text.

**Authentication required**

### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| id | string (UUID) | Yes | The unique identifier of the voice note. |

### Example Request

```bash
curl -X GET https://api.lvng.ai/api/v2/voice-notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890/transcript \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200)

```json
{
  "success": true,
  "voiceNoteId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "title": "Sprint retro thoughts",
  "transcript": "I think the sprint went well overall. The main blocker was the calendar integration taking longer than expected. We should allocate more buffer time for third-party API work in the next sprint.",
  "segments": [
    { "start": 0.0, "end": 3.2, "text": "I think the sprint went well overall." },
    { "start": 3.5, "end": 8.1, "text": "The main blocker was the calendar integration taking longer than expected." },
    { "start": 8.4, "end": 14.8, "text": "We should allocate more buffer time for third-party API work in the next sprint." }
  ],
  "status": "completed"
}
```
