Lewati ke konten
kenari.

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.

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.

RouteTingkatKey dipegang oleh
GET /v1/workspaceBacaAnggota mana pun
PATCH /v1/workspaceKelolaPemilik atau Admin
GET /v1/workspace/membersBacaAnggota mana pun
PATCH /v1/workspace/members/{user_id}KelolaPemilik atau Admin
DELETE /v1/workspace/members/{user_id}KelolaPemilik atau Admin
PUT /v1/workspace/members/{user_id}/capKelolaPemilik atau Admin
PUT /v1/workspace/member-cap-defaultKelolaPemilik atau Admin
GET /v1/workspace/invitesBacaPemilik atau Admin
POST /v1/workspace/invitesKelolaPemilik atau Admin
DELETE /v1/workspace/invites/{id}KelolaPemilik atau Admin
GET /v1/workspace/seatsBacaAnggota mana pun
POST /v1/workspace/seats/quoteBacaPemilik atau Admin
POST /v1/workspace/seatsKelola + pembayaranPemilik atau Admin
PATCH /v1/workspace/seats/{id}Kelola + pembayaranPemilik atau Admin
DELETE /v1/workspace/seats/{id}KelolaPemilik atau Admin
POST /v1/workspace/seats/{id}/cancel-changeKelolaPemilik atau Admin
POST /v1/workspace/seats/reactivateKelola + pembayaranPemilik atau Admin
POST /v1/workspace/fund-from-personalKelola + pembayaranPemilik 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.

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
}
FieldTipeKeterangan
idstringId workspace.
namestringNama tim.
kindstringteam.
rolestringPeran orang yang memegang key: owner, admin, atau member.
member_limitintegerJumlah orang terbanyak yang bisa ada di workspace. Undangan tertunda ikut dihitung.
seat_modebooleantrue kalau workspace membeli paket per kursi.
balance_micro_idrintegerSaldo workspace.
default_cap_micro_idrinteger atau nullBatas 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:

FieldTipeKeterangan
user_idstringId orang itu. Route anggota di bawah memakainya di path.
emailstringAlamat email orang itu.
rolestringowner, admin, atau member.
joined_atintegerWaktu orang itu bergabung.
last_seen_atinteger atau nullWaktu terakhir orang itu aktif.
seatobject atau nullKursi yang dipegang orang itu: plan_id, dan active yang bernilai false setelah paket workspace berhenti.
capobject atau nullBatas PAYG orang itu: cap_micro_idr, spent_micro_idr pada jendela saat ini, dan window_end. null kalau tidak ada batas.
paygobject atau nullJumlah 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.

Terminal window
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.

Route di bagian ini butuh tingkat Kelola dan key yang dipegang Pemilik atau Admin. Semuanya menerima header Idempotency-Key. Lihat Idempotensi.

PATCH /v1/workspace mengganti nama tim. Jawabannya {"workspace": {...}} dengan nama baru.

FieldTipeWajibKeterangan
namestringYaNama baru. Spasi di tepi dibuang, 1 sampai 40 karakter, tanpa karakter kontrol.

PATCH /v1/workspace/members/{user_id} mengubah peran seorang anggota. Jawabannya {"user_id": "...", "role": "..."}.

FieldTipeWajibKeterangan
rolestringYaadmin atau member.

Peran Pemilik tidak pernah berubah di sini. Kepemilikan hanya dipindahkan di dashboard.

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.

FieldTipeWajibKeterangan
keysstringYarevoke atau transfer.
transfer_tostringDengan transferuser_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.

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.

FieldTipeWajibKeterangan
cap_micro_idrinteger atau nullYaBatas 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.

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": ...}.

FieldTipeWajibKeterangan
cap_micro_idrinteger atau nullYaRentang yang sama dengan batas anggota. null menghapus batas bawaan.

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.

FieldTipeWajibKeterangan
emailstringYaAlamat yang diundang, sampai 254 karakter.
rolestringYaadmin atau member.
Terminal window
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.

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.

POST /v1/workspace/seats/quote menghitung harga pembelian dan tidak mengubah apa pun. Butuh tingkat Baca dan key yang dipegang Pemilik atau Admin.

FieldTipeWajibKeterangan
plan_idstringYaPaket yang dihitung harganya.
countintegerTidakJumlah kursi yang ditambah, 1 sampai 25. Default-nya 1. Diabaikan kalau ada seat_id.
seat_idstringTidakHitung 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.

POST /v1/workspace/seats membeli kursi dari satu paket dan menagih saldo workspace. Butuh tingkat Kelola + pembayaran, dan Idempotency-Key wajib.

FieldTipeWajibKeterangan
plan_idstringYaPaket yang dibeli.
countintegerYaJumlah kursi, 1 sampai 25.
user_idstringTidakAnggota yang mendapat kursi pertama. Kosongkan supaya kursinya belum diberikan.
pay_fromstringTidakHanya 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.

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.

FieldTipeWajibKeterangan
user_idstring atau nullTidakKalau 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_idstringTidakPaket tujuan kursi. Paket yang lebih tinggi berlaku sekarang dan menagih selisihnya untuk sisa siklus. Paket yang lebih rendah berlaku saat perpanjangan.
expect_unassignedbooleanTidakDefault-nya false. Dengan true, request ditolak dengan 409 kalau ternyata sudah ada yang memegang kursi itu, bukan mengambilnya dari orang itu.
pay_fromstringTidakHanya workspace, yang menjadi default.

Body harus memuat user_id atau plan_id. Kenaikan dibayar dari saldo workspace dan menghasilkan 402 kalau saldonya kurang.

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.

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

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.

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.

FieldTipeWajibKeterangan
amount_idrintegerYaJumlah 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.

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-Key yang sama dengan body yang sama, dalam 90 hari, mengembalikan jawaban yang tersimpan dengan header Idempotent-Replayed: true, dan tidak ada yang dijalankan lagi.
  • Idempotency-Key yang sama dengan body atau route yang berbeda menghasilkan 409 dan idempotency_key_reused.
  • Idempotency-Key yang sama saat request pertamanya masih berjalan menghasilkan 409 dan request_in_progress.
Terminal window
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.

StatusKodeKapan
400invalid_requestBody 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.
400idempotency_key_requiredRoute membutuhkan header Idempotency-Key dan request tidak membawanya.
400pay_from_personal_not_supportedRequest kursi memakai pay_from selain workspace.
401Tidak adaKey tidak ada, tidak valid, kedaluwarsa, atau sudah dicabut. Body-nya teks biasa.
402insufficient_balanceSaldo yang membayar kurang. error.gap_micro_idr menyebut kekurangannya, dan error.source bernilai personal kalau yang kurang adalah saldo Pribadi.
403workspace_access_requiredTingkat akses key lebih rendah dari yang dibutuhkan route, atau key itu adalah key yang disimpan kenari login atau key per tool.
403shared_key_not_allowedKey adalah API key dibagikan.
403restricted_key_not_allowedKey punya pembatasan model, pembatasan kemampuan berbayar, batas biaya, atau batas token.
403teams_not_enabledWorkspace tim belum tersedia untuk akun ini.
403not_a_team_workspaceWorkspace milik key bukan workspace tim.
403permission_deniedRoute itu untuk Pemilik atau Admin dan key dipegang Anggota, atau aturan workspace menolak perubahannya, misalnya mengeluarkan Pemilik.
403fund_cap_reachedPemindahan akan melewati batas harian key. Pesannya menyebut sisa yang masih bisa dipindahkan.
404not_foundAnggota, undangan, atau kursi tidak ada di workspace.
409conflictKeadaan workspace menolak perubahannya, misalnya mengundang orang yang sudah menjadi anggota, workspace yang sudah mencapai batas anggota, atau menghapus kursi terakhir.
409idempotency_key_reusedIdempotency-Key sudah dipakai untuk body atau route yang berbeda.
409request_in_progressRequest pertama dengan Idempotency-Key ini masih berjalan.
429rate_limitedPerubahan lebih banyak dari yang diizinkan batasnya. Header Retry-After menyebut berapa lama harus menunggu. Lihat Batas laju.
429pending_limit_reachedWorkspace punya terlalu banyak undangan tertunda. Cabut sebagian, lalu undang lagi.
429daily_limit_reachedWorkspace sudah mencapai batas undangan harian.
500internal_errorAda 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.

GET /v1/usage, GET /v1/usage/daily, dan GET /v1/usage/log menerima parameter user_id yang mempersempit jawaban ke satu anggota. Lihat Pemakaian.

Alur ini membaca daftar anggota lalu mengundang satu orang kalau dia belum ada di workspace.

Terminal window
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"
}'
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"])
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);
}