API workspace
Pemilik atau Admin sebuah workspace tim bisa melakukan dengan API key apa yang mereka lakukan di halaman Tim dan halaman Langganan: membaca workspace, mengundang dan mengeluarkan orang, mengubah peran, mengatur batas PAYG, membeli dan mengubah kursi, serta memindahkan saldo Pribadi ke tim. Key-nya membutuhkan akses workspace, yang kamu pilih saat membuat key. Lihat Autentikasi & API key. Menghapus workspace, memindahkan kepemilikan, keluar dari workspace, dan menerima undangan tetap dilakukan di dashboard. Endpoint ini khusus kenari dan tidak punya padanan di OpenAI maupun Anthropic.
Workspace tim terbuka untuk semua akun. Kalau suatu saat dimatikan, key tidak bisa diberi akses workspace, jadi request-nya ke API ini menjawab 403 dengan workspace_access_required. Key yang sudah punya akses workspace menjawab 403 dengan teams_not_enabled selama workspace tim ditutup.
Kirim key di header Authorization sebagai Bearer kn-..., dan kirim body JSON dengan Content-Type: application/json.
Tingkat akses
Section titled “Tingkat akses”Sebuah key punya salah satu dari empat tingkat, yang ditentukan saat key dibuat: tanpa akses, Baca, Kelola, dan Kelola + pembayaran. Setiap tingkat mencakup tingkat sebelumnya. Tabel ini mendaftar semua route, tingkat yang dibutuhkan, dan siapa yang harus memegang key.
| Route | Tingkat | Key dipegang oleh |
|---|---|---|
GET /v1/workspace | Baca | Anggota mana pun |
PATCH /v1/workspace | Kelola | Pemilik atau Admin |
GET /v1/workspace/members | Baca | Anggota mana pun |
PATCH /v1/workspace/members/{user_id} | Kelola | Pemilik atau Admin |
DELETE /v1/workspace/members/{user_id} | Kelola | Pemilik atau Admin |
PUT /v1/workspace/members/{user_id}/cap | Kelola | Pemilik atau Admin |
PUT /v1/workspace/member-cap-default | Kelola | Pemilik atau Admin |
GET /v1/workspace/invites | Baca | Pemilik atau Admin |
POST /v1/workspace/invites | Kelola | Pemilik atau Admin |
DELETE /v1/workspace/invites/{id} | Kelola | Pemilik atau Admin |
GET /v1/workspace/seats | Baca | Anggota mana pun |
POST /v1/workspace/seats/quote | Baca | Pemilik atau Admin |
POST /v1/workspace/seats | Kelola + pembayaran | Pemilik atau Admin |
PATCH /v1/workspace/seats/{id} | Kelola + pembayaran | Pemilik atau Admin |
DELETE /v1/workspace/seats/{id} | Kelola | Pemilik atau Admin |
POST /v1/workspace/seats/{id}/cancel-change | Kelola | Pemilik atau Admin |
POST /v1/workspace/seats/reactivate | Kelola + pembayaran | Pemilik atau Admin |
POST /v1/workspace/fund-from-personal | Kelola + pembayaran | Pemilik atau Admin |
Key bertindak sebagai orang yang membuatnya, dengan peran orang itu pada saat setiap request. Key selalu dibuat oleh Pemilik atau Admin, tetapi perannya bisa berubah setelahnya. Kalau orang itu diturunkan menjadi Anggota, route yang bertanda Pemilik atau Admin menolak key tersebut dengan 403 dan permission_denied, sedangkan route yang bertanda Anggota mana pun tetap berjalan. Kalau orang itu keluar atau dikeluarkan dari workspace, key dicabut, juga saat key lainnya diserahkan ke anggota lain.
Baca workspace
Section titled “Baca workspace”Setiap field _micro_idr di API ini dalam mikro-Rupiah, yaitu Rupiah dikali 1.000.000. Rp 1 adalah 1000000. Waktu dalam detik Unix. Lihat Cara penagihan.
GET /v1/workspace mengembalikan workspace tempat key berada. Butuh tingkat Baca. Di workspace Pribadi, route ini hanya mengembalikan id, kind (personal), dan role.
{ "id": "5b0e3a52-6f56-4c52-9d6f-0f3f3f4a9c11", "name": "Tim Konten", "kind": "team", "role": "admin", "member_limit": 25, "seat_mode": true, "balance_micro_idr": 250000000000, "default_cap_micro_idr": 100000000000}| Field | Tipe | Keterangan |
|---|---|---|
id | string | Id workspace. |
name | string | Nama tim. |
kind | string | team. |
role | string | Peran orang yang memegang key: owner, admin, atau member. |
member_limit | integer | Jumlah orang terbanyak yang bisa ada di workspace. Undangan tertunda ikut dihitung. |
seat_mode | boolean | true kalau workspace membeli paket per kursi. |
balance_micro_idr | integer | Saldo workspace. |
default_cap_micro_idr | integer atau null | Batas PAYG yang didapat anggota baru. null kalau belum diatur, dan selalu null untuk Anggota. |
GET /v1/workspace/members mengembalikan members dan default_cap_micro_idr, nilainya sama seperti di atas. Setiap anggota punya field berikut:
| Field | Tipe | Keterangan |
|---|---|---|
user_id | string | Id orang itu. Route anggota di bawah memakainya di path. |
email | string | Alamat email orang itu. |
role | string | owner, admin, atau member. |
joined_at | integer | Waktu orang itu bergabung. |
last_seen_at | integer atau null | Waktu terakhir orang itu aktif. |
seat | object atau null | Kursi yang dipegang orang itu: plan_id, dan active yang bernilai false setelah paket workspace berhenti. |
cap | object atau null | Batas PAYG orang itu: cap_micro_idr, spent_micro_idr pada jendela saat ini, dan window_end. null kalau tidak ada batas. |
payg | object atau null | Jumlah yang dipakai orang itu dari saldo workspace dalam jendela 30 hari, ada batas atau tidak: spent_micro_idr dan window_end. |
Key yang dipegang Anggota hanya membaca cap dan payg pada barisnya sendiri. Baris lain berisi null di sana.
curl https://kenari.id/v1/workspace/members \ -H "Authorization: Bearer $KENARI_API_KEY"{ "members": [ { "user_id": "usr_8d1f6c0e-3a74-4b0a-8f0e-6f1b2c9d4e21", "email": "owner@example.com", "role": "owner", "joined_at": 1788300000, "last_seen_at": 1790890000, "seat": { "plan_id": "plan-studio-v3", "active": true }, "cap": null, "payg": { "spent_micro_idr": 12000000000, "window_end": 1790892000 } } ], "default_cap_micro_idr": 100000000000}GET /v1/workspace/invites mengembalikan undangan yang tertunda, dari yang terbaru: id, email, role, expires_at, dan created_at. Key yang dipegang Pemilik atau Admin bisa membacanya. Token undangan tidak pernah dikembalikan, karena hanya dikirim lewat email penerima undangan.
GET /v1/workspace/seats mengembalikan seat_mode, cycle_end, dan seats. Key yang dipegang Pemilik atau Admin juga mendapat plan_status, auto_renew, renewal_total_micro_idr, dan semua kursi beserta email pemegangnya. Key yang dipegang Anggota hanya mendapat kursinya sendiri. Setiap kursi punya id, plan_id, plan_name, user_id (null kalau kursi belum diberikan), email (email pemegangnya, null kalau kursi belum diberikan), next_plan_id (paket tujuan saat perpanjangan), remove_at_renewal, weekly_reset_available (apakah reset kuota mingguan kursi itu belum dipakai), dan windows. windows berisi pemakaian dan anggaran tiap jendela kuota dalam mikro-Rupiah, yaitu cost_5h dan limit_5h, cost_week dan limit_week, cost_month dan limit_month, serta jumlah detik sampai tiap jendela direset, yaitu resets_5h_secs, resets_week_secs, dan resets_month_secs.
Field personal_* menampilkan saldo Pribadi orang yang memegang key. Field ini hanya muncul untuk key Kelola + pembayaran, dan di bawah tingkat itu field-nya tidak disertakan. GET /v1/workspace/seats membawa personal_balance_micro_idr.
Kelola anggota dan undangan
Section titled “Kelola anggota dan undangan”Route di bagian ini butuh tingkat Kelola dan key yang dipegang Pemilik atau Admin. Semuanya menerima header Idempotency-Key. Lihat Idempotensi.
Ganti nama workspace
Section titled “Ganti nama workspace”PATCH /v1/workspace mengganti nama tim. Jawabannya {"workspace": {...}} dengan nama baru.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
name | string | Ya | Nama baru. Spasi di tepi dibuang, 1 sampai 40 karakter, tanpa karakter kontrol. |
Ubah peran
Section titled “Ubah peran”PATCH /v1/workspace/members/{user_id} mengubah peran seorang anggota. Jawabannya {"user_id": "...", "role": "..."}.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
role | string | Ya | admin atau member. |
Peran Pemilik tidak pernah berubah di sini. Kepemilikan hanya dipindahkan di dashboard.
Keluarkan anggota
Section titled “Keluarkan anggota”DELETE /v1/workspace/members/{user_id} mengeluarkan seorang anggota dan, dalam langkah yang sama, menentukan nasib key yang dipegang orang itu di workspace ini. Request ini membawa body JSON, dan body-nya wajib.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
keys | string | Ya | revoke atau transfer. |
transfer_to | string | Dengan transfer | user_id anggota lain yang menerima key. |
Dengan revoke, semua key yang dipegang orang itu di sini dicabut dan berhenti bekerja. Dengan transfer, key tetap berjalan di bawah transfer_to, dan setiap key lalu bertindak sebagai anggota itu. Key dengan akses workspace justru dicabut, karena key itu bertindak sebagai orang yang membuatnya. API key dibagikan yang diserahkan ke seorang Anggota juga dicabut. Jawabannya {"removed": true, "keys_revoked": 0, "keys_transferred": 3}, dengan jumlah yang sebenarnya.
Pemilik tidak bisa dikeluarkan. Sebuah key tidak bisa mengeluarkan orang yang memegangnya, karena keluar dari workspace dilakukan di dashboard.
Atur batas PAYG anggota
Section titled “Atur batas PAYG anggota”PUT /v1/workspace/members/{user_id}/cap mengatur atau menghapus batas PAYG satu anggota. Batas ini adalah jumlah terbanyak yang bisa dipakai orang itu dari saldo workspace dalam 30 hari berjalan: pemakaian PAYG di luar kuota kursi, pencarian web, gambar, video, dan biaya lain. Jawabannya {"user_id": "...", "cap": {...}}, dengan cap berisi field yang sama seperti di daftar anggota, atau null setelah batas dihapus.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
cap_micro_idr | integer atau null | Ya | Batas dalam mikro-Rupiah, kelipatan 1000000, dari 1000000 (Rp 1) sampai 1000000000000000 (Rp 1.000.000.000). null menghapus batas. |
Field ini harus ada. Body tanpa field ini ditolak, jadi body kosong tidak pernah menghapus batas secara tidak sengaja. Perubahan tidak pernah menyentuh pemakaian yang sudah dihitung di jendela itu. Pemilik tidak punya batas, dan batas seseorang diatur oleh orang lain, jadi key Admin tidak bisa mengatur atau menghapus batas pemegangnya sendiri.
Atur batas bawaan untuk anggota baru
Section titled “Atur batas bawaan untuk anggota baru”PUT /v1/workspace/member-cap-default mengatur batas PAYG yang didapat seseorang saat menerima undangan mulai sekarang. Anggota yang sudah ada di workspace tetap memakai batas yang mereka punya. Jawabannya {"default_cap_micro_idr": ...}.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
cap_micro_idr | integer atau null | Ya | Rentang yang sama dengan batas anggota. null menghapus batas bawaan. |
Undang anggota
Section titled “Undang anggota”POST /v1/workspace/invites mengundang sebuah alamat email. kenari mengirim undangan lewat email atas nama orang yang memegang key, dan undangan berlaku 7 hari. Jawabannya 201.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
email | string | Ya | Alamat yang diundang, sampai 254 karakter. |
role | string | Ya | admin atau member. |
curl https://kenari.id/v1/workspace/invites \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "dewi@example.com", "role": "member" }'{ "id": "0c9d7e1a-52b3-4f6e-9a7d-3e8b1f2a4c55", "email": "dewi@example.com", "role": "member", "expires_at": 1791497000, "email_sent": true}email_sent bernilai false kalau email tidak bisa terkirim. Undangannya tetap ada, jadi cabut lalu undang lagi. Mengundang alamat yang sudah punya undangan tertunda menggantikan undangan itu. Alamat yang sudah menjadi anggota, atau workspace yang sudah mencapai batas anggota, menghasilkan 409.
Satu workspace bisa menyimpan 50 undangan tertunda dan membuat 20 undangan dalam 24 jam mana pun, sama seperti batas di dashboard. Kalau melewatinya, request gagal dengan 429 dan pending_limit_reached atau daily_limit_reached. Batas ini dihitung untuk workspace, jadi menambah key tidak menaikkannya.
Cabut undangan
Section titled “Cabut undangan”DELETE /v1/workspace/invites/{id} mencabut undangan yang tertunda, dan tautannya berhenti bekerja. id adalah id dari daftar undangan. Jawabannya {"revoked": true}.
Kursi adalah slot paket berbayar di sebuah tim. Tingkat yang dibutuhkan tiap route ada di tabel di atas. plan_id adalah id sebuah paket dari GET /api/plans, yang tidak membutuhkan key. Lihat Langganan.
Kursi yang dibeli lewat API dibayar dari saldo workspace. pay_from apa pun selain "workspace", termasuk "personal", ditolak dengan 400 dan pay_from_personal_not_supported. Untuk membayar dengan uangmu sendiri, pindahkan dulu saldo Pribadi ke workspace. Lihat Pindahkan saldo Pribadi ke tim.
Kursi ditagih dengan harga pada saat kamu membelinya. Kalau harga berubah setelah kamu mengecek harga, pembelian menagih harga yang baru, jadi cek harga tepat sebelum membeli.
Cek harga pembelian
Section titled “Cek harga pembelian”POST /v1/workspace/seats/quote menghitung harga pembelian dan tidak mengubah apa pun. Butuh tingkat Baca dan key yang dipegang Pemilik atau Admin.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
plan_id | string | Ya | Paket yang dihitung harganya. |
count | integer | Tidak | Jumlah kursi yang ditambah, 1 sampai 25. Default-nya 1. Diabaikan kalau ada seat_id. |
seat_id | string | Tidak | Hitung harga menaikkan kursi ini ke plan_id, bukan menambah kursi. |
{ "charge_micro_idr": 199000000000, "saldo_micro_idr": 250000000000, "gap_micro_idr": 0}charge_micro_idr adalah jumlah yang ditagih pembelian sekarang. saldo_micro_idr adalah saldo workspace, dan gap_micro_idr adalah kekurangannya, 0 kalau saldo cukup untuk tagihan. Key Kelola + pembayaran juga mendapat personal_saldo_micro_idr, yaitu saldo Pribadi orang yang memegang key, dan personal_gap_micro_idr, yaitu kekurangannya.
Beli kursi
Section titled “Beli kursi”POST /v1/workspace/seats membeli kursi dari satu paket dan menagih saldo workspace. Butuh tingkat Kelola + pembayaran, dan Idempotency-Key wajib.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
plan_id | string | Ya | Paket yang dibeli. |
count | integer | Ya | Jumlah kursi, 1 sampai 25. |
user_id | string | Tidak | Anggota yang mendapat kursi pertama. Kosongkan supaya kursinya belum diberikan. |
pay_from | string | Tidak | Hanya workspace, yang menjadi default. |
{ "ok": true, "seat_ids": ["3f2c1a90-7d4e-4b8a-9c16-5e0a8b7d2f13"], "charged_micro_idr": 199000000000, "seat_mode": true}Saldo workspace yang kurang menghasilkan 402 dan insufficient_balance, dengan error.gap_micro_idr.
Ubah kursi
Section titled “Ubah kursi”PATCH /v1/workspace/seats/{id} memberikan kursi ke seorang anggota, mengosongkan kursi dari pemegangnya, menaikkan paketnya sekarang, atau menurunkan paketnya saat perpanjangan. Butuh tingkat Kelola + pembayaran untuk semua isi body, karena memberikan dan menaikkan memakai route yang sama dan kenaikan menagih biaya. Idempotency-Key wajib. Jawabannya {"ok": true, "charged_micro_idr": 0}, dengan jumlah 0 kalau tidak ada yang ditagih.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
user_id | string atau null | Tidak | Kalau tidak diisi, pemegang tetap sama. null melepas kursi dari pemegangnya. user_id seorang anggota memberikan kursi kepadanya. Satu orang hanya bisa memegang satu kursi. |
plan_id | string | Tidak | Paket tujuan kursi. Paket yang lebih tinggi berlaku sekarang dan menagih selisihnya untuk sisa siklus. Paket yang lebih rendah berlaku saat perpanjangan. |
expect_unassigned | boolean | Tidak | Default-nya false. Dengan true, request ditolak dengan 409 kalau ternyata sudah ada yang memegang kursi itu, bukan mengambilnya dari orang itu. |
pay_from | string | Tidak | Hanya workspace, yang menjadi default. |
Body harus memuat user_id atau plan_id. Kenaikan dibayar dari saldo workspace dan menghasilkan 402 kalau saldonya kurang.
Hapus kursi
Section titled “Hapus kursi”DELETE /v1/workspace/seats/{id} menandai kursi untuk dihapus pada perpanjangan berikutnya. Butuh tingkat Kelola. Jawabannya {"ok": true}. Hasilnya 409 kalau penghapusan itu membuat workspace tanpa kursi.
Batalkan perubahan yang tertunda
Section titled “Batalkan perubahan yang tertunda”POST /v1/workspace/seats/{id}/cancel-change menghapus paket lebih rendah yang tertunda dan penghapusan yang tertunda pada kursi itu. Butuh tingkat Kelola. Jawabannya {"ok": true}.
Aktifkan lagi kursi
Section titled “Aktifkan lagi kursi”POST /v1/workspace/seats/reactivate membeli lagi kursi sebuah workspace yang sudah berhenti. Route ini menagih total perpanjangan dari saldo workspace, menerapkan perubahan yang tertunda, dan memulai siklus baru. Perpanjangan lebih awal untuk siklus yang masih aktif tidak ada. Butuh tingkat Kelola + pembayaran, dan Idempotency-Key wajib.
Body diabaikan, kecuali pay_from selain workspace yang ditolak. Jawabannya berisi ok, charged_micro_idr, dan cycle_end. Saldo yang kurang menghasilkan 402.
Pindahkan saldo Pribadi ke tim
Section titled “Pindahkan saldo Pribadi ke tim”POST /v1/workspace/fund-from-personal memindahkan uang dari saldo Pribadi orang yang memegang key ke workspace. Butuh tingkat Kelola + pembayaran, dan Idempotency-Key wajib. Pemindahan ini searah: uang tidak pernah keluar dari tim.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
amount_idr | integer | Ya | Jumlah dalam Rupiah utuh, dari 1000 sampai 100000000. Field ini dalam Rupiah, bukan mikro-Rupiah. |
{ "ok": true, "amount_micro_idr": 500000000000, "balance_micro_idr": 750000000000, "personal_balance_micro_idr": 120000000000}amount_micro_idr adalah jumlah yang dipindahkan. balance_micro_idr adalah saldo workspace setelah pemindahan, dan personal_balance_micro_idr adalah saldo Pribadi setelahnya.
Setiap key punya batas harian untuk jumlah yang boleh dipindahkan, dihitung dalam 24 jam berjalan. Kamu mengaturnya saat membuat key. Lihat Autentikasi & API key. Pemindahan yang akan melewati batas itu ditolak dengan 403 dan fund_cap_reached, dan pesannya menyebut sisa yang masih bisa dipindahkan. Saldo Pribadi yang kurang menghasilkan 402, dengan error.gap_micro_idr dan error.source bernilai personal. Pemindahan yang berakhir dengan 500, atau terputus sebelum ada jawaban, mungkin sudah memindahkan uangnya, jadi jumlahnya tetap dihitung terhadap batas selama 24 jam berikutnya. Baca saldonya lewat route GET sebelum mencoba lagi.
kenari mengirim email ke orang yang memegang key setelah setiap pemindahan, berisi jumlahnya, workspace, dan key yang dipakai.
Idempotensi
Section titled “Idempotensi”Kirim header Idempotency-Key berisi 1 sampai 128 karakter ASCII yang terlihat, artinya tanpa spasi. Header ini wajib di empat route: POST /v1/workspace/seats, PATCH /v1/workspace/seats/{id}, POST /v1/workspace/seats/reactivate, dan POST /v1/workspace/fund-from-personal. Tanpa header ini, keempatnya menjawab 400 dan idempotency_key_required. Perubahan lainnya juga menerima header ini.
Idempotency-Keyyang sama dengan body yang sama, dalam 90 hari, mengembalikan jawaban yang tersimpan dengan headerIdempotent-Replayed: true, dan tidak ada yang dijalankan lagi.Idempotency-Keyyang sama dengan body atau route yang berbeda menghasilkan409danidempotency_key_reused.Idempotency-Keyyang sama saat request pertamanya masih berjalan menghasilkan409danrequest_in_progress.
curl https://kenari.id/v1/workspace/seats \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "plan_id": "plan-studio-v3", "count": 1 }'Pakai Idempotency-Key baru untuk setiap pembelian atau pemindahan. Pakai nilai yang sama hanya saat kamu mengulang request yang sama, misalnya setelah timeout, supaya ulangan itu tidak menagih dua kali. Menjalankan perintah di atas lagi membuat nilai baru, jadi simpan nilainya sebelum mengirim request kalau kamu mungkin perlu mengulang.
Jawaban yang tersimpan bisa berupa error. Mengulang pembelian yang ditolak mengembalikan penolakan yang sama, jadi setelah kamu memperbaiki penyebabnya, misalnya saldo yang kurang, kirim percobaan berikutnya dengan Idempotency-Key baru. Request yang terputus di tengah bisa tetap berstatus berjalan. Baca keadaannya dengan route GET, dan kalau perubahannya tidak terjadi, kirim lagi dengan Idempotency-Key baru.
Error memakai format error /v1, dan kamu menentukan penanganannya dari error.code. Pengecualiannya 401: key yang tidak ada, tidak valid, kedaluwarsa, atau sudah dicabut mendapat body teks singkat, bukan JSON.
{ "error": { "message": "This key does not have the workspace access this route needs.", "type": "permission_error", "param": null, "code": "workspace_access_required" }}type mengikuti status: invalid_request_error untuk 400, insufficient_balance untuk 402, permission_error untuk 403, not_found_error untuk 404, conflict_error untuk 409, rate_limit_error untuk 429, dan server_error untuk 500. Error 500 selalu membawa pesan internal error.
Pesan error berbahasa Inggris. Kirim x-kenari-locale: id, atau header Accept-Language yang bahasa pertamanya id, untuk mendapat bahasa Indonesia pada error yang berasal dari aturan yang sama dengan dashboard. Error yang dibuat API ini sendiri, seperti error akses, idempotensi, dan batas di bawah, selalu berbahasa Inggris.
| Status | Kode | Kapan |
|---|---|---|
| 400 | invalid_request | Body bukan JSON yang valid untuk route itu, sebuah nilai ditolak, id di path bukan 1 sampai 128 karakter huruf, angka, tanda hubung, atau garis bawah, atau header Idempotency-Key tidak valid. |
| 400 | idempotency_key_required | Route membutuhkan header Idempotency-Key dan request tidak membawanya. |
| 400 | pay_from_personal_not_supported | Request kursi memakai pay_from selain workspace. |
| 401 | Tidak ada | Key tidak ada, tidak valid, kedaluwarsa, atau sudah dicabut. Body-nya teks biasa. |
| 402 | insufficient_balance | Saldo yang membayar kurang. error.gap_micro_idr menyebut kekurangannya, dan error.source bernilai personal kalau yang kurang adalah saldo Pribadi. |
| 403 | workspace_access_required | Tingkat akses key lebih rendah dari yang dibutuhkan route, atau key itu adalah key yang disimpan kenari login atau key per tool. |
| 403 | shared_key_not_allowed | Key adalah API key dibagikan. |
| 403 | restricted_key_not_allowed | Key punya pembatasan model, pembatasan kemampuan berbayar, batas biaya, atau batas token. |
| 403 | teams_not_enabled | Workspace tim belum tersedia untuk akun ini. |
| 403 | not_a_team_workspace | Workspace milik key bukan workspace tim. |
| 403 | permission_denied | Route itu untuk Pemilik atau Admin dan key dipegang Anggota, atau aturan workspace menolak perubahannya, misalnya mengeluarkan Pemilik. |
| 403 | fund_cap_reached | Pemindahan akan melewati batas harian key. Pesannya menyebut sisa yang masih bisa dipindahkan. |
| 404 | not_found | Anggota, undangan, atau kursi tidak ada di workspace. |
| 409 | conflict | Keadaan workspace menolak perubahannya, misalnya mengundang orang yang sudah menjadi anggota, workspace yang sudah mencapai batas anggota, atau menghapus kursi terakhir. |
| 409 | idempotency_key_reused | Idempotency-Key sudah dipakai untuk body atau route yang berbeda. |
| 409 | request_in_progress | Request pertama dengan Idempotency-Key ini masih berjalan. |
| 429 | rate_limited | Perubahan lebih banyak dari yang diizinkan batasnya. Header Retry-After menyebut berapa lama harus menunggu. Lihat Batas laju. |
| 429 | pending_limit_reached | Workspace punya terlalu banyak undangan tertunda. Cabut sebagian, lalu undang lagi. |
| 429 | daily_limit_reached | Workspace sudah mencapai batas undangan harian. |
| 500 | internal_error | Ada yang gagal di kenari. Perubahannya mungkin sudah terjadi atau belum, jadi baca keadaannya dengan route GET sebelum mencoba lagi dengan Idempotency-Key baru. |
Lihat Error untuk kode lainnya.
Pemakaian per anggota
Section titled “Pemakaian per anggota”GET /v1/usage, GET /v1/usage/daily, dan GET /v1/usage/log menerima parameter user_id yang mempersempit jawaban ke satu anggota. Lihat Pemakaian.
Contoh
Section titled “Contoh”Alur ini membaca daftar anggota lalu mengundang satu orang kalau dia belum ada di workspace.
curl https://kenari.id/v1/workspace/members \ -H "Authorization: Bearer $KENARI_API_KEY"
curl https://kenari.id/v1/workspace/invites \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "dewi@example.com", "role": "member" }'Python
Section titled “Python”import os
import requests
base = "https://kenari.id/v1/workspace"headers = {"Authorization": f"Bearer {os.environ['KENARI_API_KEY']}"}email = "dewi@example.com"
members = requests.get(f"{base}/members", headers=headers)members.raise_for_status()
if any(member["email"].lower() == email for member in members.json()["members"]): print("Sudah menjadi anggota")else: invite = requests.post( f"{base}/invites", headers=headers, json={"email": email, "role": "member"}, ) invite.raise_for_status() print("Undangan terkirim, berlaku sampai", invite.json()["expires_at"])JavaScript
Section titled “JavaScript”const base = "https://kenari.id/v1/workspace";const headers = { Authorization: `Bearer ${process.env.KENARI_API_KEY}` };const email = "dewi@example.com";
const membersResponse = await fetch(`${base}/members`, { headers });if (!membersResponse.ok) throw new Error(await membersResponse.text());const { members } = await membersResponse.json();
if (members.some((member) => member.email.toLowerCase() === email)) { console.log("Sudah menjadi anggota");} else { const inviteResponse = await fetch(`${base}/invites`, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ email, role: "member" }), }); if (!inviteResponse.ok) throw new Error(await inviteResponse.text()); const invite = await inviteResponse.json(); console.log("Undangan terkirim, berlaku sampai", invite.expires_at);}