---
title: POST /recordings/{id}/download-url
source: https://se-hari.com/docs/api/api-recordings-download-url
updated: 2026-08-15T18:52:41.883762+00:00
---

> Buat tautan unduh rekaman. POST /recordings/{id}/download-url — butuh scope recordings:read.

Menghasilkan URL bertanda tangan untuk mengunduh rekaman, berlaku satu jam. Berbeda dengan jalur unduh di dashboard, endpoint ini tidak memeriksa User-Agent maupun Origin, sehingga bisa dipakai skrip, cron, dan workflow otomasi. Parameter `asset` memilih berkas: `video` untuk rekaman utama, `audio` untuk berkas audio terpisah yang lebih kecil dan lebih cocok diproses mesin. Tidak semua rekaman punya berkas audio terpisah — periksa `has_audio_asset` lebih dulu. Rekaman yang sudah lewat masa retensi menjawab 410, bukan 404: datanya memang pernah ada, dan membedakan keduanya membantu Anda memutuskan apakah perlu mengarsipkan rekaman ke penyimpanan sendiri sebelum kedaluwarsa.
## Endpoint
```bash
POST https://se-hari.com/api/v1/recordings/{id}/download-url
```
Butuh scope `recordings:read`. Scope ditetapkan saat key dibuat dan **tidak bisa ditambahkan belakangan** — kalau key Anda kurang izin, buat key baru lalu cabut yang lama. Kekakuan ini disengaja: scope yang bisa dinaikkan diam-diam berarti key yang bocor hari ini bisa menjadi lebih berbahaya besok. Beri setiap integrasi key sendiri dengan scope sesempit mungkin, supaya mencabut satu integrasi tidak pernah berarti mematikan yang lain.
Kirim header `Idempotency-Key` berisi nilai unik milik Anda sendiri; UUID sudah cukup. Kalau request diulang dengan key yang sama — karena timeout jaringan, retry otomatis n8n, atau tombol yang terklik dua kali — kami mengembalikan hasil yang pertama alih-alih mengerjakannya lagi. Key yang sama dengan body berbeda ditolak `idempotency_key_reused`, karena itu hampir selalu berarti bug di sisi pemanggil, bukan permintaan yang sah.
Operasi ini tidak memotong kredit saat dipanggil. Kalau ia mengantre pekerjaan notulen, penagihan terjadi **setelah** pemrosesan selesai dan dihitung dari durasi asli rekaman — bukan dari durasi yang Anda perkirakan. Perbedaan ini penting saat membaca saldo di langkah berikutnya sebuah workflow.
## Parameter path

| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| `id` | `string` | ya | min 1 karakter |

## Body request

| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
| `asset` | `video` \| `audio` | tidak | — |

## Contoh
```bash
curl -X POST "https://se-hari.com/api/v1/recordings/c0ffee00-1111-2222-3333-444455556666/download-url" \
  -H "Authorization: Bearer $SEHARI_API_KEY"
```
```javascript
const res = await fetch('https://se-hari.com/api/v1/recordings/c0ffee00-1111-2222-3333-444455556666/download-url', {
	method: 'POST',
	headers: {
			'Authorization': `Bearer ${process.env.SEHARI_API_KEY}`,
		}
});

if (!res.ok) {
	const { error } = await res.json();
	// error.code stabil dan bisa dicabang; error.message untuk manusia.
	throw new Error(`${error.code}: ${error.message}`);
}

const data = await res.json();
```
```python
import os, requests

res = requests.post(
    "https://se-hari.com/api/v1/recordings/c0ffee00-1111-2222-3333-444455556666/download-url",
    headers={"Authorization": f"Bearer {os.environ['SEHARI_API_KEY']}"},
    timeout=30,
)

if not res.ok:
    err = res.json()["error"]
    raise RuntimeError(f"{err['code']}: {err['message']}")

data = res.json()
```
## Field respons

| Field | Tipe | Keterangan |
|---|---|---|
| `url` | `string` | — |
| `asset` | `string` | — |
| `expires_at` | `string` | — |
| `size_mb` | `number` \| `null` | — |
| `file_name` | `string` \| `null` | — |

## Kalau gagal
Setiap kegagalan memakai amplop yang sama, dan `code` di dalamnya stabil — cabangkan logika Anda ke sana, jangan ke `message` yang teksnya bisa diperbaiki sewaktu-waktu.
```json
{
  "error": {
    "code": "invalid_api_key",
    "message": "Penjelasan untuk manusia.",
    "docs_url": "https://se-hari.com/docs/error/invalid-api-key",
    "request_id": "req_a1b2c3d4e5f6",
    "details": {}
  }
}
```
| Status | Kode | Artinya di endpoint ini |
|---|---|---|
| 401 | [`invalid_api_key`](/docs/error/invalid-api-key) | Key salah, sudah dicabut, atau kedaluwarsa. |
| 403 | [`insufficient_scope`](/docs/error/insufficient-scope) | Key valid, tapi tidak punya scope yang dibutuhkan endpoint ini. |
| 404 | [`not_found`](/docs/error/not-found) | Data tidak ada, atau ada tapi milik akun lain — keduanya dijawab sama. |
| 409 | [`conflict`](/docs/error/conflict) | Bentrok dengan kondisi data sekarang. |
| 410 | [`gone`](/docs/error/gone) | Data pernah ada tapi sudah lewat masa simpan. |
| 429 | [`rate_limited`](/docs/error/rate-limited) | Terlalu banyak request. Hormati header `Retry-After`. |
| 500 | [`internal_error`](/docs/error/internal-error) | Kesalahan di sisi kami. Sertakan `request_id` saat melapor. |
Simpan `request_id` dari setiap respons gagal. Itu satu-satunya cara kami menemukan kembali request Anda di log ketika Anda melapor, dan ia juga ada di header `X-Request-Id` pada respons yang berhasil.
## Alur khas

Rekaman disimpan **30 hari** lalu dihapus otomatis. Angka itu menentukan hampir semua keputusan di sekitar endpoint rekaman: tautan unduh dibuat sesaat, arsip harus dijadwalkan, dan rekaman lama menjawab `gone` alih-alih menyajikan berkas yang sudah tidak ada.

1. `GET /recordings` — cari rekaman yang Anda butuhkan
2. `POST /recordings/{id}/download-url` — buat tautan bertanda tangan
3. Unduh berkasnya SEGERA; tautan berlaku singkat dan tidak bisa disimpan untuk nanti
4. Salin ke penyimpanan Anda sendiri kalau perlu disimpan lebih dari 30 hari

Tautan unduh sengaja berumur pendek karena ia memberi akses tanpa API key kepada siapa pun yang memegangnya. Menyimpannya di database atau mengirimnya lewat email sama dengan membagikan isi rekaman kepada siapa saja yang kelak membaca catatan itu.

## Yang sering keliru

- **Key disalin sebagian.** API key Se-Hari panjangnya tetap: `sh_live_` diikuti 43 karakter. Kalau panjangnya berbeda, yang salah adalah proses penyalinannya, bukan key-nya — dan pesan 401 tidak akan mengatakan itu.
- **Scope kurang, bukan key salah.** 403 di sini berarti key-nya dikenali. Periksa scope-nya lewat `GET /me`, lalu buat key baru kalau memang kurang; scope tidak bisa ditambahkan ke key yang sudah jadi.
- **404 tidak selalu berarti data tidak ada.** Data milik akun lain juga dijawab 404, bukan 403 — kami tidak mengonfirmasi keberadaan id milik orang lain. Kalau Anda yakin id-nya benar, periksa apakah key yang dipakai milik akun yang sama.
- **409 biasanya berarti pekerjaannya sudah ada.** Cek `details` pada respons: di sana ada id data yang sudah lebih dulu menempati posisi itu, dan biasanya itulah yang sebenarnya Anda cari.

## Selanjutnya
- [Autentikasi dan API key](/docs/autentikasi) — cara membuat key dan memilih scope
- [Kode error](/docs/error) — arti setiap kode dan cara memperbaikinya
- [Idempotency](/docs/idempotency) — kenapa retry aman kalau dilakukan dengan benar
- [Spesifikasi OpenAPI](https://se-hari.com/api/v1/openapi.json) — kontrak mesin, cocok untuk men-generate klien