Langsung ke konten

REST API ​

Konsol dibangun di atas REST API JSON di https://zenavo.app/api. Apa pun yang bisa Anda lakukan di konsol, bisa Anda lakukan dengan API ini.

Autentikasi ​

Kirim API key sebagai bearer token:

sh
curl -H "Authorization: Bearer $ZENAVO_API_KEY" https://zenavo.app/api/auth/me

Izin dan batas proyek kunci menentukan endpoint mana yang boleh dipanggilnya; yang lain menjawab 403. Tagihan, pembelian, keanggotaan tim, dan /api/account/* hanya untuk sesi browser.

Permintaan dari sesi browser juga memerlukan header X-Zenavo-Request: 1 pada apa pun selain GET; permintaan dengan bearer token tidak.

Permintaan hanya memerlukan sesi atau API key yang valid dengan peran yang sesuai; tidak ada aksi yang meminta konfirmasi kedua. Akun yang belum memverifikasi email mendapat 403 dengan {"email_unverified": true} untuk perubahan di luar akunnya sendiri.

Konvensi ​

  • Body berupa JSON. Waktu dalam detik Unix.
  • ID diberi prefiks: team_, srv_, proj_, job_, bak_, tok_.
  • Error mengembalikan kode status dan {"detail": …}. Error validasi (422) mencantumkan setiap field.
  • Operasi panjang (deploy, restart, backup) mengembalikan job: {"id": "job_…"}. Poll GET /api/jobs/{id} untuk status (queued, running, succeeded, failed), log, dan progress per layanan.

Endpoint ​

Akun ​

MetodePath
GET/api/auth/mePengguna yang sedang masuk, timnya, dan email_verified
PATCH/api/accountNama dan avatar
GET POST/api/account/tokensDaftar atau buat API key (hanya sesi browser). Buat dengan {name, days, scope, project_ids}: scope adalah read, deploy, atau full (bawaan); project_ids membatasi kunci ke proyek tersebut (hilangkan atau null untuk semua)
DELETE/api/account/tokens/{id}Hapus API key
GET/api/account/sessionsSesi yang sedang masuk
POST/api/auth/2faLangkah masuk kedua: {code} setelah /api/auth/login menjawab {two_factor: true}
POST/api/account/verifyKonfirmasi bahwa ini Anda: {code} dengan autentikasi dua faktor, selain itu {password}. Saat ini tidak ada aksi yang memerlukannya
POST/api/auth/verify-emailBuka tautan verifikasi email: {token} (berlaku sekali, selama 24 jam)
POST/api/account/email/verifyKirim tautan verifikasi baru; {verified: true} bila alamat sudah terverifikasi
GET/api/account/2faStatus dua faktor: enabled (aplikasi atau passkey), totp, kode pemulihan yang tersisa, jumlah passkey
POST/api/account/2fa/setup /enable /disable /recovery-codesKelola autentikasi dua faktor (hanya sesi browser)
POST/api/auth/2fa/passkey/options lalu /api/auth/2fa/passkeyLangkah masuk kedua dengan passkey: opsi permintaan WebAuthn ({options: null} tanpa passkey) dan apakah akun punya aplikasi autentikator (totp), lalu {credential}
GET/api/account/passkeysPasskey Anda: nama, AAGUID, disinkronkan, dibuat, terakhir dipakai
POST/api/account/passkeys/options lalu /api/account/passkeysDaftarkan passkey: opsi pembuatan, lalu {name, credential} (faktor kedua pertama akun juga mengembalikan recovery_codes)
DELETE/api/account/passkeys/{id}Hapus passkey (menghapus faktor kedua terakhir mematikan autentikasi dua faktor)
GET/api/account/audit?before=&limit=Aktivitas keamanan Anda sendiri
POST/api/auth/device/codeMulai login CLI (device flow): {client_name, scope, client} → {device_code, user_code, verification_uri, expires_in, interval}. scope bawaannya full; client adalah cli (bawaan) atau mcp, dan menamai kunci "CLI on …" atau "MCP on …"
POST/api/auth/device/tokenPoll dengan {device_code}: 202 selama menunggu, 429 bila di-poll lebih cepat dari interval, 200 {token, scope, project_ids} setelah disetujui (penyetuju mungkin mempersempit cakupannya)

Tim ​

MetodePath
GET POST/api/teamsDaftar atau buat tim
PATCH/api/teams/{tid}Ganti nama
GET/api/teams/{tid}/membersAnggota
PATCH DELETE/api/teams/{tid}/members/{uid}Ubah peran, hapus anggota
GET POST/api/teams/{tid}/invitesUndangan
GET/api/teams/{tid}/audit?before=&limit=Log audit (admin)
GET POST/api/teams/{tid}/notification-channelsChannel notifikasi (admin). Buat dengan {kind, name, events, url} (Slack, Discord, webhook) atau {kind: "telegram", name, events, bot_token, chat_id}; secret webhook dikembalikan sekali
PATCH DELETE/api/teams/{tid}/notification-channels/{cid}Ubah name, events, enabled, atau tujuan; hapus
POST/api/teams/{tid}/notification-channels/{cid}/testKirim pesan uji sekarang
POST/api/teams/{tid}/notification-channels/{cid}/secretSigning secret baru untuk webhook (dikembalikan sekali)
GET POST/api/teams/{tid}/log-drainsLog drain (admin; membuat memerlukan paket Pro ke atas). Buat dengan {kind: "http", name, url}, {kind: "datadog", name, site, api_key}, atau {kind: "loki", name, url, username, password, tenant_id}, ditambah projects (kosong untuk semua); secret drain HTTP dikembalikan sekali
PATCH DELETE/api/teams/{tid}/log-drains/{did}Ubah name, projects, enabled, atau tujuan; hapus
POST/api/teams/{tid}/log-drains/{did}/testKirim baris uji sekarang
POST/api/teams/{tid}/log-drains/{did}/secretSigning secret baru untuk drain HTTP (dikembalikan sekali)

Penggunaan dan tagihan ​

MetodePath
GET/api/teams/{tid}/usage?period=YYYY-MMBatas paket, server, anggota, menit build per proyek, layanan, dan hari
GET/api/teams/{tid}/usage/builds.csv?period=Setiap build terukur dalam sebulan, sebagai CSV
GET/api/teams/{tid}/transferTransfer image periode ini: bytes, included_bytes, overage_cents, limit_cents, blocked, percent, serta awal dan akhir periode
GET/api/teams/{tid}/billingPaket, status langganan, kursi, dan katalog paket (admin)
GET/api/teams/{tid}/billing/invoicesFaktur terbaru dengan tautan halaman Stripe dan PDF (admin)
GET/api/teams/{tid}/server-billingServer yang dibeli beserta bulan yang sudah dibayar dan perpanjangan otomatisnya, dan tagihan server terbaru (admin)
POST/api/teams/{tid}/billing/checkoutMulai langganan: {plan} → {url} Stripe Checkout (pemilik)
POST/api/teams/{tid}/billing/changeGanti langganan ke paket lain: {plan} (pemilik)
POST/api/teams/{tid}/billing/cancel /resumeBatalkan di akhir periode, atau urungkan (pemilik)
PUT/api/teams/{tid}/billing/spend-limitAtur batas pengeluaran: {cents}, 0 hingga 1.000.000 (pemilik)
POST/api/teams/{tid}/billing/portal{url} Customer Portal Stripe untuk kartu, detail tagihan, dan faktur (pemilik)

Server ​

MetodePath
GET/api/servers?team_id=Daftar server
POST/api/servers?team_id=Tambahkan server: {name, host, port, user, password | private_key}
GET PATCH/api/servers/{sid}Baca, ganti nama
POST/api/servers/{sid}/bootstrapJalankan penyiapan lagi: {password | private_key}, atau body kosong dalam sehari setelah penyiapan gagal untuk memakai lagi kredensial yang disimpan
PATCH/api/servers/{sid}/billingPerpanjangan otomatis server yang dibeli: {auto_renew} (admin). GET /api/servers/{sid} menampilkannya di bought, dengan paid_until
POST/api/servers/{sid}/renewBayar bulan berikutnya server yang dibeli sekarang (admin)
GET/api/servers/{sid}/usage?hours=Riwayat CPU, memori, dan disk
GET/api/servers/{sid}/systemKomponen platform
GET/api/servers/{sid}/portsPemeriksaan port
GET/api/servers/{sid}/jobsPenyiapan dan job lainnya
POST/api/servers/{sid}/deleteHapus: {confirmation: "<server name>"}
POST/api/servers/{sid}/reinstallCopot K3s dan siapkan server dari awal: {confirmation: "<server name>", password | private_key} (admin; server tidak boleh punya proyek). Menjawab dengan server, yang penyiapannya masuk antrean
POST/api/servers/{sid}/reinstall-osPasang ulang sistem operasi server yang dibeli dan siapkan dari awal, tanpa kredensial SSH; kata sandi login baru dikirim ke email pemanggil: {confirmation: "<server name>"} (admin; server tidak boleh punya proyek, dan bought.reinstall_os-nya harus true). Menjawab dengan server, yang pemasangan ulangnya masuk antrean
GET/api/servers/{sid}/ssh-logins?hours=&failed=Riwayat login SSH (member)
POST/api/servers/{sid}/agentPindahkan server yang ditambahkan sebelum ada agen ke agen (admin)
POST/api/servers/{sid}/agent/updatePerbarui agen (admin; mengembalikan job)
GET/api/servers/{sid}/firewallFirewall server (member). Untuk server yang dibeli: aturan tetap Zenavo (web, HTTPS, ping, SSH untuk penyiapan) dan aturan tim, diterapkan di penyedia; untuk server yang Anda hubungkan: port yang perlu dibuka di sana
POST/api/servers/{sid}/firewall/rulesTambahkan aturan izin ke server yang dibeli: {protocol, ports, sources, note}, dengan protocol tcp, udp, atau icmp, ports seperti 8080 atau 30000-30100, dan sources daftar alamat atau rentang CIDR (admin; hingga 30 aturan)
PUT DELETE/api/servers/{sid}/firewall/rules/{rid}Ubah aturan (body yang sama) atau hapus (admin)
POST/api/servers/{sid}/firewall/applyTerapkan lagi aturan di penyedia setelah gagal (admin)

Proyek ​

MetodePath
GET/api/projects?team_id=Daftar proyek
POST/api/projectsBuat: {team_id, server_id, manifest}
GET/api/projects/{pid}Ringkasan, layanan, dan status
GET/api/projects/{pid}/manifestKonfigurasi lengkap, dengan variabel
PUT/api/projects/{pid}Simpan konfigurasi: {manifest, server_id, revision}
POST/api/projects/{pid}/deployDeploy semua layanan (mengembalikan job)
POST/api/projects/{pid}/rollback{target_job}
POST/api/projects/{pid}/services/{service}/rollback{target_job}: kembalikan satu layanan ke deployment itu; yang lain tetap seperti yang ter-deploy (mengembalikan job)
POST/api/projects/{pid}/restart /stopRestart atau hentikan semua layanan
GET/api/projects/{pid}/runtimeStatus langsung setiap layanan
POST/api/projects/{pid}/clone /transferKloning: {name, team_id, server_id}; atau pindahkan proyek yang belum pernah di-deploy ke tim lain: {team_id, server_id} (admin kedua tim)
GET/api/projects/{pid}/movePemindahan ke server lain terbaru
GET/api/projects/{pid}/move/preflight?server_id=Periksa pemindahan tanpa mengubah apa pun (admin)
POST/api/projects/{pid}/movePindahkan, beserta datanya, ke server lain di tim: {server_id, confirmation: "<project name>"} (admin; mengembalikan job)
POST/api/projects/{pid}/move/finish /move/rollbackHapus salinan lama, atau kembali ke server lama (admin; mengembalikan job)
POST/api/projects/{pid}/delete{confirmation: "<project name>"}
PUT/api/projects/{pid}/checklistChecklist migrasi: {source_backup, data_restored, application_verified, dns_switched, rollback_ready}, masing-masing true atau false (member)

Layanan ​

{service} adalah ID layanan; nama saat ini juga bisa dipakai. Di konfigurasi proyek, setiap layanan punya id dan hostname opsional; hilangkan keduanya untuk layanan baru dan Zenavo memberinya id svc-… acak dan hostname yang dibuat dari namanya.

MetodePath
POST/api/projects/{pid}/services/{service}/deployDeploy layanan ini saja; yang lain tetap seperti yang ter-deploy (mengembalikan job)
PUT/api/projects/{pid}/services/{service}/source?create=&port=&deploy=&acknowledge_init=Unggah tar terkompresi gzip dari sebuah direktori (hingga 500 MB) sebagai sumber layanan dan deploy (mengembalikan {upload_id, service, created, job}); create=1 menambahkan layanan yang belum ada, deploy=0 hanya menyimpannya
POST/api/projects/{pid}/services/{service}/restart /suspend /resume
POST/api/projects/{pid}/services/{service}/execJalankan perintah di kontainer
GET/api/projects/{pid}/services/{service}/terminalWebSocket: terminal interaktif (member). Frame biner membawa input dan output; frame teks membawa JSON: {"type":"resize","cols","rows"} dari klien, {"type":"exit","code"} atau {"type":"error","message"} dari server. Browser harus mengirim Origin konsol
GET/api/projects/{pid}/services/{service}/files?path=Daftar isi folder di kontainer (member)
GET/api/projects/{pid}/services/{service}/files/content?path=256 KB pertama sebuah file, sebagai teks
GET/api/projects/{pid}/services/{service}/files/download?path=Unduh file (hingga 500 MB)
PUT/api/projects/{pid}/services/{service}/files/upload?dir=&name=Unggah body mentah (hingga 50 MB, Content-Length wajib); &overwrite=1 mengganti
POST/api/projects/{pid}/services/{service}/files/mkdirBuat folder: {"path"}
POST/api/projects/{pid}/services/{service}/files/renameGanti nama di tempat: {"path", "name"}
POST/api/projects/{pid}/services/{service}/files/deleteHapus file atau folder: {"path"}
GET/api/projects/{pid}/services/{service}/usage?hours=Riwayat CPU dan memori
GET/api/projects/{pid}/logs/{service}Log runtime terbaru
GET/api/projects/{pid}/logs/{service}/streamLog runtime langsung (Server-Sent Events)
GET/api/projects/{pid}/logs/{service}/history?from=&to=&q=&cursor=&limit=Log runtime tersimpan, dari yang terlama: baris yang berisi q (tanpa membedakan huruf besar-kecil) antara from dan to (RFC 3339 atau detik Unix; bawaannya satu jam terakhir). Kembalikan next sebagai cursor untuk mendapatkan lebih banyak
GET/api/projects/{pid}/logs/{service}/download?from=&to=&q=Log runtime tersimpan dalam suatu rentang sebagai file teks
POST/api/projects/{pid}/services/{service}/domainsBuat domain otomatis
DELETE/api/projects/{pid}/domains/{domain}Hapus domain otomatis

Tampilan database ​

Tampilan database layanan database. Member boleh membaca; penulisan memerlukan admin tim. Setiap permintaan berjalan di dalam kontainer database dan dicatat di log audit tanpa SQL, kunci, nilai, atau dokumennya. … adalah /api/projects/{pid}/services/{service}.

MetodePath
GET…/database/tablesPostgreSQL, MySQL, MariaDB: tabel dan view, dengan perkiraan baris dan kolom
GET…/database/rows?schema=&table=&offset=&limit=Satu halaman baris tabel
POST…/database/queryJalankan satu statement SQL: {sql, allow_writes, max_rows}. Read-only kecuali allow_writes (admin)
POST…/database/keysRedis, Valkey: daftar kunci dengan SCAN: {db, match, cursor, count}
POST…/database/keyNilai satu kunci: {db, key, cursor, count}, atau key_b64 untuk kunci yang bukan UTF-8
POST…/database/commandJalankan satu perintah: {db, command, allow_writes}. Hanya perintah baca kecuali allow_writes (admin)
GET…/database/collectionsMongoDB: database dan koleksi, dengan perkiraan jumlah dokumen
POST…/database/findCari dokumen: {db, collection, filter, sort, projection, skip, limit}, semuanya string JSON; 50 per halaman secara bawaan
POST…/database/documentUbah satu dokumen: {op, db, collection, filter, update, document}, dengan op insert, update, atau delete (admin)

Backup ​

MetodePath
GET/api/backups?project_id=Daftar backup
POST/api/projects/{pid}/backupsBackup sebuah layanan: {service, engine}
GET/api/backups/{bid}/downloadUnduh
POST/api/backups/{bid}/restorePulihkan: {confirmation}, seperti yang diketik di konsol

Git dan GitHub ​

MetodePath
POST/api/git/refsBranch dan tag repositori publik: {url}
POST/api/git/planPratinjau rencana build: {team_id, url, ref, context}
GET/api/github/repos?team_id=Repositori yang bisa dibaca instalasi GitHub tim
GET/api/github/branches?team_id=&repo=Branch salah satunya

Image dan domain ​

MetodePath
GET/api/images/ports?team_id=&image=Port yang di-EXPOSE image publik, dibaca dari registry-nya tanpa menarik image
GET/api/images/mirror?image=Apakah mirror Zenavo untuk Tiongkok daratan memiliki suatu image, dan alamatnya di sana
GET/api/images/mirror/search?q=Image di mirror yang namanya mengandung q
GET/api/domains/available?name=Apakah nama domain otomatis masih tersedia: {available, reason}

Template ​

MetodePath
GET/api/templatesGaleri template (publik, tanpa kunci)
GET/api/templates/{code}Satu template dan manifest-nya (publik)
POST/api/imports/previewPeriksa template Zeabur atau ekspor Zenavo: {text, team_id, variables}, atau template galeri: {template, team_id, variables}
POST/api/imports/{did}/commitBuat proyek: {server_id}

Contoh: deploy lalu tunggu ​

sh
JOB=$(curl -s -X POST -H "Authorization: Bearer $ZENAVO_API_KEY" \
  -H "Content-Type: application/json" -d '{}' \
  https://zenavo.app/api/projects/$PROJECT/deploy | jq -r .id)

until curl -s -H "Authorization: Bearer $ZENAVO_API_KEY" https://zenavo.app/api/jobs/$JOB \
  | jq -e '.status == "succeeded" or .status == "failed"' >/dev/null; do sleep 5; done

Tidak berafiliasi dengan Zeabur.