Memasukkan sebuah rekaman yang sudah tersimpan di Se-Hari ke antrean notulen AI. Ini verb paling bernilai di API: rekaman meeting Zoom Anda sudah otomatis diarsipkan, dan endpoint ini mengubah arsip itu jadi ringkasan tanpa perlu mengunggah apa pun. Gunakan GET /recordings?has_note=false untuk menemukan rekaman yang belum pernah diproses. Endpoint ini TIDAK memotong kredit saat dipanggil — ia hanya mengantre. Kredit ditagih setelah notulen selesai, sesuai durasi asli rekaman. Satu rekaman hanya boleh punya satu notulen; memanggil ulang untuk rekaman yang sama menjawab 409 beserta id notulen yang sudah ada. Pemrosesan berjalan asinkron: pantau lewat GET /notes/{id} atau daftarkan webhook note.completed.
#Endpoint
POST https://se-hari.com/api/v1/notesButuh scope notulen:write. 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 memakai kredit. Panggil POST /credits/estimate lebih dulu kalau perlu tahu biayanya di depan, dan pasang batas kredit harian pada API key di dashboard. Batas itu adalah pengaman termurah yang tersedia: kalau key bocor atau sebuah loop salah tulis, kerugian Anda berhenti di angka yang Anda tentukan sendiri, bukan di saldo yang habis.
#Body request
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
recording_id | string | ya | min 1 karakter |
title | string | tidak | min 1 karakter, maks 200 karakter |
language | id | en | tidak | Bahasa notulen; default id |
#Contoh
curl -X POST "https://se-hari.com/api/v1/notes" \
-H "Authorization: Bearer $SEHARI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recording_id":"3a91c0de-1f2b-4c5d-8e9f-0a1b2c3d4e5f","title":"Rapat Klien Agustus"}'const res = await fetch('https://se-hari.com/api/v1/notes', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SEHARI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"recording_id": "3a91c0de-1f2b-4c5d-8e9f-0a1b2c3d4e5f",
"title": "Rapat Klien Agustus"
})
});
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();import os, requests
res = requests.post(
"https://se-hari.com/api/v1/notes",
headers={"Authorization": f"Bearer {os.environ['SEHARI_API_KEY']}"},
json={"recording_id":"3a91c0de-1f2b-4c5d-8e9f-0a1b2c3d4e5f","title":"Rapat Klien Agustus"},
timeout=30,
)
if not res.ok:
err = res.json()["error"]
raise RuntimeError(f"{err['code']}: {err['message']}")
data = res.json()#Respons 202
{
"id": "8f2c1b3a-4d5e-6f70-8192-a3b4c5d6e7f8",
"status": "recording_ready",
"recording_id": "3a91c0de-1f2b-4c5d-8e9f-0a1b2c3d4e5f",
"estimated_ready_at": "2026-08-15T06:40:00.000Z",
"queue_position": 2
}#Field respons
| Field | Tipe | Keterangan |
|---|---|---|
id | string | — |
status | string | — |
recording_id | string | — |
estimated_ready_at | string | null | — |
queue_position | integer | — |
#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.
{
"error": {
"code": "invalid_request",
"message": "Penjelasan untuk manusia.",
"docs_url": "https://se-hari.com/docs/error/invalid-request",
"request_id": "req_a1b2c3d4e5f6",
"details": {}
}
}| Status | Kode | Artinya di endpoint ini |
|---|---|---|
| 400 | invalid_request | Body tidak lolos validasi. Field yang bermasalah disebut di details. |
| 401 | invalid_api_key | Key salah, sudah dicabut, atau kedaluwarsa. |
| 403 | insufficient_scope | Key valid, tapi tidak punya scope yang dibutuhkan endpoint ini. |
| 404 | not_found | Data tidak ada, atau ada tapi milik akun lain — keduanya dijawab sama. |
| 409 | conflict | Bentrok dengan kondisi data sekarang. |
| 410 | gone | Data pernah ada tapi sudah lewat masa simpan. |
| 429 | rate_limited | Terlalu banyak request. Hormati header Retry-After. |
| 500 | 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
Notulen selalu berawal dari sebuah rekaman. Rekaman itu bisa datang sendiri (meeting Zoom yang direkam diarsipkan otomatis) atau Anda unggah sendiri. Setelah rekaman ada, pembuatan notulen berjalan asinkron: endpoint mengantre pekerjaan lalu langsung menjawab, dan hasilnya menyusul beberapa menit kemudian.
GET /recordings?has_note=false— cari rekaman yang belum pernah diprosesPOST /notesdenganrecording_iddari langkah 1 — antrekan pekerjaannya- Tunggu webhook
note.completed, atau pollingGET /notes/{id}sampaistatusmenjadicompleted GET /notes/{id}?include=summary,action_items— ambil hasilnya
Polling bukan pilihan pertama. Rekaman satu jam butuh beberapa menit untuk diproses, dan menanyakan statusnya setiap detik hanya menghabiskan kuota rate limit Anda. Daftarkan webhook note.completed sekali, lalu biarkan Se-Hari yang memberi tahu.
#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
detailspada 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 — cara membuat key dan memilih scope
- Kode error — arti setiap kode dan cara memperbaikinya
- Idempotency — kenapa retry aman kalau dilakukan dengan benar
- Spesifikasi OpenAPI — kontrak mesin, cocok untuk men-generate klien